Compare commits

...
90 Commits
Author SHA1 Message Date
Dan Mulloy adb3c5392c Release version 4.3.0 2017-06-14 14:30:46 -04:00
Dan Mulloy cd2f56d7c7 Update TinyProtocol to 1.12 2017-06-14 14:30:31 -04:00
Dan Mulloy 7c9bbc3f6c Add 1.12 protocol version 2017-06-09 15:08:04 -04:00
Dan Mulloy 52e396b25d Update to Spigot 1.12 2017-06-08 13:30:57 -04:00
Dan Mulloy aeb2cfb65e Update to 1.12-pre6
No real changes this time
2017-05-30 14:31:13 -04:00
Dan Mulloy 7f5ea6e493 Merge remote-tracking branch 'origin/master' 2017-05-29 22:14:28 -04:00
Dan Mulloy 1be94aad78 Rework packet type deprecation to actually work properly
Also fix compatibility with 1.8.0
2017-05-29 22:14:08 -04:00
dadus33 6bb169ac01 Fix 1.8 compatibility (#339)
Use proper method name ('setLenient' instead of 'setLenienent') in the deserializeLegacy method for 1.8 compatibility.
2017-05-29 12:59:01 -04:00
Dan Mulloy ea7900d64f Better ensure packet types are correct, deprecate legacy IDs 2017-05-24 17:20:27 -04:00
Dan Mulloy 123abdff30 Code cleanup, remove some ancient compatibility 2017-05-24 14:35:22 -04:00
Dan Mulloy 11247f1c01 Return an empty modifier if a class does not exist
Addresses #336
2017-05-23 18:52:28 -04:00
Dan Mulloy f093c9196d Ignore IntelliJ files 2017-05-19 11:17:31 -04:00
Dan Mulloy 81daa62071 Update to 1.12-pre5 2017-05-19 11:11:30 -04:00
Dan Mulloy f99b977888 Restore backwards compatibility, restructure poms 2017-05-18 12:02:47 -04:00
Dan Mulloy b00ae2ff15 Remove Java 8 warning
It's required, so the JVM will warn you for me
2017-05-14 21:46:18 -04:00
Dan Mulloy b4f9c501b3 1.12 hotfix: delay injection for network manager
Fixes #332, fixes #330
2017-05-14 21:37:36 -04:00
Dan Mulloy 1738c6f243 Stop testing with Java 7 2017-05-14 14:37:55 -04:00
Dan Mulloy abea7441a4 Merge branch 'master' of git+ssh://git@github.com/dmulloy2/ProtocolLib 2017-05-14 14:30:21 -04:00
Dan Mulloy 3887ba27d2 Update to the 1.12 pre-release
- Now requires Java 8
- Added new packets
- Added ChatType enum (replaces magic value)
- Missing protocol version
2017-05-14 14:28:56 -04:00
games647 76a3af84a1 Strictly match the sendPacket method
Related to #322
2017-04-27 20:49:37 -04:00
Dan Mulloy be5d42ee19 Minecraft 1.12 will use Java 8 2017-04-27 20:48:02 -04:00
Dan Mulloy 790711c16e Update version to 4.2.2-SNAPSHOT 2017-04-27 20:46:04 -04:00
Dan Mulloy 8c3127dee1 Release version 4.2.1 2017-04-09 11:56:53 -04:00
Dan Mulloy 5ec87c9d76 Fix issues with logging custom payload packets 2017-03-20 21:42:46 -04:00
Dan Mulloy 2205ce639e Merge pull request #305 from aadnk/master
Branch hiccup
2017-03-18 19:33:14 -04:00
Dan Mulloy 2844d75607 Merge pull request #127 from dmulloy2/master
Branch hiccup
2017-03-18 19:32:12 -04:00
Dan Mulloy a6810ea874 Merge branch 'master' of git+ssh://git@github.com/dmulloy2/ProtocolLib 2017-03-18 19:29:43 -04:00
Dan Mulloy aaf1af8e41 Don't worry if we can't determine the Java version 2017-03-18 19:29:16 -04:00
Dan Mulloy b8dd43aeed Merge pull request #126 from joeypoulissen/master
Added missing fallingdust particle as seen at http://wiki.vg/Protocol#Particle
2017-03-12 18:42:29 -04:00
Joey e959efc5af Added fallingdust particle as seen at http://wiki.vg/Protocol#Particle 2017-03-11 13:59:25 +01:00
Dan Mulloy 73ce01bbde Make packet logging more robust 2017-03-09 10:53:06 -05:00
Dan Mulloy b30628f6db Make hex dump more readable, match more packet names 2017-03-08 09:08:29 -05:00
Dan Mulloy 9289825d87 Add packet logging for debugging 2017-03-01 17:02:46 -05:00
Dan Mulloy 57cfbc4f81 Merge branch 'master' of git+ssh://git@github.com/dmulloy2/ProtocolLib 2017-02-04 12:12:54 -05:00
Dan Mulloy 3ab2026d62 People don't care if updater sites are down 2017-02-04 12:12:31 -05:00
Dan Mulloy e439c778a6 Use the correct JsonReader constructor in 1.8
Fixes aadnk#121
2017-01-01 16:13:47 -05:00
Dan Mulloy 98413c917e Update packet IDs 2016-12-22 12:24:18 -05:00
Dan Mulloy 540a5e53c4 Make Sound handling more robust
Fixes aadnk#119
2016-12-22 11:42:52 -05:00
Dan Mulloy 27047f83a5 Fix erroneous startup message 2016-12-22 11:33:55 -05:00
Dan Mulloy 222b535c15 Update version to 4.2.1-SNAPSHOT 2016-12-22 11:33:10 -05:00
Dan Mulloy 3f31af6b70 I'll look into signing releases later 2016-12-21 14:20:56 -05:00
Dan Mulloy b6a054b766 Release version 4.2.0 2016-12-21 14:16:57 -05:00
Dan Mulloy 3b7b692e80 Update to 1.11.2 2016-12-21 14:02:32 -05:00
Dan Mulloy c6788addeb Mark 1.11.1 as officially supported 2016-12-20 23:37:35 -05:00
Dan Mulloy 5b38bc7f38 Update to 1.11.1 2016-12-20 16:14:13 -05:00
Dan Mulloy 33bb12713d Restore ItemStack to CraftItemStack conversion 2016-12-16 12:36:35 -05:00
Dan Mulloy d175a7655e Refactor package caching, account for some edge cases 2016-11-29 19:39:11 -05:00
Dan Mulloy 4afb38aae1 Merge pull request #115 from Mast3rPlan/master
Fix performance issue
2016-11-27 12:41:02 -05:00
Emiel Tasseel 36b5670b3d Fix performance issue where classes that don't exist in the current environment endlessly get looked up over and over again 2016-11-26 12:50:27 +01:00
Dan Mulloy 7b61796506 Add mechanism to clone NonNullLists 2016-11-22 17:36:49 -05:00
Dan Mulloy 684b687e42 Rework ItemStack handling for 1.11
Note to developers: You should never supply a null ItemStack to any
method. ProtocolLib will never return a null ItemStack. Update your
plugins accordingly.
2016-11-20 16:29:23 -05:00
Dan Mulloy 7a9b9afe2e Add modifier for item lists 2016-11-19 19:18:35 -05:00
Dan Mulloy 7d382279ad Update to Minecraft 1.11 2016-11-16 21:56:46 -05:00
Dan Mulloy b4f5f18283 Strongly recommend Java 8
If you must use Java 7, set 'ignoreJava: true' in the config.
2016-10-14 19:28:17 -04:00
Dan Mulloy 17e150393a Avoid throwing errors with invalid entities 2016-10-14 19:20:47 -04:00
Dan Mulloy b5ecfb9c5f Update version to 4.2.0-SNAPSHOT 2016-10-14 19:20:16 -04:00
Dan Mulloy 31a0c9b10c Update version in plugin.yml 2016-09-18 15:27:49 -04:00
Dan Mulloy e4d95e09cf Update version to 4.1.0 for release 2016-09-18 15:27:01 -04:00
Dan Mulloy d173be0db5 Actually use UpdateSign when it exists 2016-09-18 14:42:40 -04:00
Dan Mulloy 051989a396 Merge pull request #265 from faradize/master
Added the ability of reading/writing lists of NBT classes!
2016-09-11 17:00:25 -04:00
Dan Mulloy 795c77e943 Fix minor example issue
Closes aadnk#113
2016-09-11 16:56:05 -04:00
Dan Mulloy 1de6bbc9dc Make sure we use the update sign packet when it exists
Should fix #263
2016-09-11 16:52:45 -04:00
verbuchselt 474c74b1f9 Added the ability of reading/writing lists of NBT classes!
Works perfectly with MapChunk-Packet's TileEntity-Data!
2016-09-07 11:37:30 +02:00
Dan Mulloy caaa57c915 Add basic TileEntityTest 2016-08-27 13:08:22 -04:00
Dan Mulloy 588f736348 Fix missing TileEntity write methods
Fixes #249
2016-08-27 13:07:49 -04:00
Dan Mulloy 828302150a Deal with plugins that create proxy player objects
As I was writing this I realized it sounded like Bungee support, but it
isn't
2016-08-13 13:19:13 -04:00
Dan Mulloy 144723af18 Ensure we're always using leniency with components
Fixes #252
2016-08-06 14:40:49 -04:00
Dan Mulloy 087913ab3a Standardize equals and hashCode for wrappers 2016-08-06 13:19:14 -04:00
Dan Mulloy 64942cbb5e Make much of ProtocolLib locale independent
Fixes #242
2016-07-25 14:09:23 -04:00
Dan Mulloy 97430a8e42 Add renamed OPEN_SIGN_ENTITY packet for backwards compat
Fixes #243
2016-07-25 13:47:56 -04:00
Dan Mulloy 1bdd200794 Try to fix a weird null case 2016-07-16 16:03:01 -04:00
Dan Mulloy 679035c3bf Fix an error with wire packets
Addresses #224
2016-07-16 15:46:23 -04:00
Dan Mulloy bd4352f041 Fix cloning data watchers
Addresses #224
2016-07-12 16:15:46 -04:00
Dan Mulloy 05ffeb8e7f Restore compatibility with 1.8
1.8.8 is still one of the most popular server versions. As a result,
many servers were not receiving important fixes and new APIs.

I was able to accomplish this with minimal bloat, making it worth it.

Upon release, 4.1.0 will become the recommended version for 1.8 thru the
current Spigot build.
2016-07-12 13:37:55 -04:00
Dan Mulloy 6c982a83f0 Cache null classes
This should prevent multiple unnecessary calls to the expensive
loadClass method

Fixes #236
2016-07-09 15:32:35 -04:00
Dan Mulloy 9860fbaec6 Fix API versioning 2016-07-07 12:19:10 -04:00
Dan Mulloy 26c29d9c85 Add conversion methods to WirePacket 2016-07-06 15:47:43 -04:00
Dan Mulloy 242b25eb1e Wrap all serializable objects in data watchers 2016-07-06 15:47:24 -04:00
Dan Mulloy c13b8d4fc6 Add modifiers for UUIDs and Directions 2016-07-06 15:46:42 -04:00
Dan Mulloy 12e9c08a1a Update version to 4.1.0-SNAPSHOT 2016-07-06 15:44:41 -04:00
Dan Mulloy 98a0528806 Update version to 4.0.2 for release 2016-07-06 13:42:57 -04:00
Dan Mulloy 015a6067fd Replace some debug info with the actual cause 2016-07-05 22:54:25 -04:00
Dan Mulloy e34105e754 Rework the component array modifier to work in 1.9.4 and up
Fixes #215
2016-07-03 13:12:24 -04:00
Dan Mulloy a995866d5b Add block data to the cloning mechanism 2016-06-30 12:34:13 -04:00
Dan Mulloy a88347dfe6 Add some more specific error messages for entity tracking
Addresses #229
2016-06-30 12:22:48 -04:00
Dan Mulloy 4f404a41c6 Update to 1.10.2
No changes, yay!
2016-06-26 23:06:00 -04:00
Dan Mulloy 88444dcf74 Fix another entity issue with Paper
Fixes #220
2016-06-20 20:50:26 -04:00
Dan Mulloy 4f5ab9eb8c Wrap and unwrap values in Optionals 2016-06-20 20:41:40 -04:00
Dan Mulloy f51427b63c Update to 1.10 2016-06-08 22:28:53 -04:00
Dan Mulloy cdcb6a6007 Update version to 4.0.2-SNAPSHOT 2016-06-08 11:12:46 -04:00
86 changed files with 3057 additions and 1654 deletions

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@
.settings/
bin/
.idea/
.iml
*.iml
.ipr
.iws
-1
View File
@@ -1,7 +1,6 @@
language: java
jdk:
- oraclejdk8
- oraclejdk7
script: mvn clean test
#before_install: cd ProtocolLib
install: true
+1 -1
View File
@@ -47,7 +47,7 @@ You can also add ProtocolLib as a Maven dependency:
<url>http://repo.dmulloy2.net/content/groups/public/</url>
</repository>
...
</repository>
</repositories>
<dependencies>
<dependency>
+12 -62
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib-API</artifactId>
<name>ProtocolLib-API</name>
<version>4.0-SNAPSHOT</version>
<version>${minorVersion}</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
@@ -19,6 +19,8 @@
<properties>
<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>
@@ -40,8 +42,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
@@ -54,7 +56,7 @@
<manifestEntries>
<Main-Class>com.comphenix.protocol.Application</Main-Class>
<Implementation-Title>ProtocolLib</Implementation-Title>
<Implementation-Version>${project.version}${project.build.number}</Implementation-Version>
<Implementation-Version>${project.fullVersion}</Implementation-Version>
<Implementation-Vendor>dmulloy2</Implementation-Vendor>
</manifestEntries>
</archive>
@@ -88,6 +90,7 @@
</activation>
<properties>
<project.build.number>-b${env.BUILD_NUMBER}</project.build.number>
<project.fullVersion>${project.version}${project.build.number}</project.fullVersion>
</properties>
</profile>
@@ -99,6 +102,9 @@
<value>true</value>
</property>
</activation>
<properties>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
<plugins>
<plugin>
@@ -129,6 +135,7 @@
</executions>
</plugin>
<!-- TODO: Look into signing releases
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
@@ -143,6 +150,7 @@
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
@@ -184,62 +192,4 @@
<timezone>1</timezone>
</developer>
</developers>
<dependencies>
<!-- Included with Minecraft
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
</dependency>
-->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.9.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.9.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.comphenix.executors</groupId>
<artifactId>BukkitExecutors</artifactId>
<version>1.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -1,9 +1,14 @@
package com.comphenix.protocol;
import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
@@ -15,7 +20,6 @@ import org.bukkit.Bukkit;
import com.comphenix.protocol.PacketTypeLookup.ClassLookup;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.ObjectEnum;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Objects;
@@ -31,12 +35,12 @@ import com.google.common.util.concurrent.Futures;
* Note that vanilla Minecraft reuses packet IDs per protocol (ping, game, login) and IDs are subject to change, so they are not reliable.
* @author Kristian
*/
public class PacketType implements Serializable, Comparable<PacketType> {
public class PacketType implements Serializable, Cloneable, Comparable<PacketType> {
// Increment whenever the type changes
private static final long serialVersionUID = 1L;
/**
* Represents an unknown legacy packet ID.
* Represents an unknown packet ID.
*/
public static final int UNKNOWN_PACKET = -1;
@@ -51,7 +55,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Incoming packets.
* @author Kristian
*/
public static class Client extends ObjectEnum<PacketType> {
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType SET_PROTOCOL = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "SetProtocol");
@@ -59,7 +63,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(PacketType.class); }
private Client() { super(); }
public static Client getInstance() {
return INSTANCE;
@@ -73,10 +77,10 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* An empty enum, as the server will not send any packets in this protocol.
* @author Kristian
*/
public static class Server extends ObjectEnum<PacketType> {
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
private final static Server INSTANCE = new Server();
private Server() { super(PacketType.class); }
private Server() { super(); }
public static Server getInstance() {
return INSTANCE;
@@ -102,7 +106,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Outgoing packets.
* @author Kristian
*/
public static class Server extends ObjectEnum<PacketType> {
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");
@@ -142,10 +146,10 @@ public class PacketType implements Serializable, Comparable<PacketType> {
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 REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x25, 0x15, "RelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x26, 0x17, "RelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x27, 0x16, "EntityLook");
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 0x28, 0x14, "Entity");
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");
@@ -153,64 +157,97 @@ public class PacketType implements Serializable, Comparable<PacketType> {
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 ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x30, 0x13, "EntityDestroy");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x31, 0x1E, "RemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x32, 0x48, "ResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x33, 0x07, "Respawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x34, 0x19, "EntityHeadRotation");
public static final PacketType WORLD_BORDER = new PacketType(PROTOCOL, SENDER, 0x35, 0x44, "WorldBorder");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x36, 0x43, "Camera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x37, 0x09, "HeldItemSlot");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x38, 0x3D, "ScoreboardDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x39, 0x1C, "EntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x3A, 0x1B, "AttachEntity");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x3B, 0x12, "EntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x3C, 0x04, "EntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x3D, 0x1F, "Experience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x3E, 0x06, "UpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x3F, 0x3B, "ScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x40, 0xFB, "Mount");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x41, 0x3E, "ScoreboardTeam");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x42, 0x3C, "ScoreboardScore");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x43, 0x05, "SpawnPosition");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x44, 0x03, "UpdateTime");
public static final PacketType TITLE = new PacketType(PROTOCOL, SENDER, 0x45, 0x45, "Title");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x47, 0x29, "NamedSoundEffect");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x48, 0x47, "PlayerListHeaderFooter");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x49, 0x0D, "Collect");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x4A, 0x18, "EntityTeleport");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x4B, 0x20, "UpdateAttributes");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x4C, 0x1D, "EntityEffect");
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");
// ---- Removed in 1.9
/**
* @deprecated Replaced by {@link WINDOW_DATA}
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType CRAFT_PROGRESS_BAR = WINDOW_DATA;
public static final PacketType MAP_CHUNK_BULK = new PacketType(PROTOCOL, SENDER, 255, 255, "MapChunkBulk");
/**
* @deprecated Replaced by {@link REL_ENTITY_MOVE_LOOK}
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType ENTITY_MOVE_LOOK = REL_ENTITY_MOVE_LOOK;
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 254, 254, "SetCompression");
/**
* @deprecated Replaced by {@link STATISTIC}
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType STATISTICS = STATISTIC;
public static final PacketType UPDATE_ENTITY_NBT = new PacketType(PROTOCOL, SENDER, 253, 253, "UpdateEntityNBT");
// ----- Renamed packets
/**
* @deprecated Replaced by {@link TILE_ENTITY_DATA}
* @deprecated Renamed to {@link #WINDOW_DATA}
*/
@Deprecated
public static final PacketType UPDATE_SIGN = TILE_ENTITY_DATA;
public static final PacketType CRAFT_PROGRESS_BAR = WINDOW_DATA.clone();
/**
* @deprecated Renamed to {@link #REL_ENTITY_MOVE_LOOK}
*/
@Deprecated
public static final PacketType ENTITY_MOVE_LOOK = REL_ENTITY_MOVE_LOOK.clone();
/**
* @deprecated Renamed to {@link #STATISTIC}
*/
@Deprecated
public static final PacketType STATISTICS = STATISTIC.clone();
/**
* @deprecated Renamed to {@link #OPEN_SIGN_EDITOR}
*/
@Deprecated
public static final PacketType OPEN_SIGN_ENTITY = OPEN_SIGN_EDITOR.clone();
// ----- Replaced in 1.9.4
/**
* @deprecated Replaced by {@link #TILE_ENTITY_DATA}
*/
@Deprecated
public static final PacketType UPDATE_SIGN = MinecraftReflection.signUpdateExists() ? new PacketType(PROTOCOL, SENDER, 252, 252, "UpdateSign") :
TILE_ENTITY_DATA.clone();
// The instance must
private final static Server INSTANCE = new Server();
// Prevent accidental construction
private Server() { super(PacketType.class); }
private Server() { super(); }
public static Sender getSender() {
return SENDER;
@@ -224,44 +261,47 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Incoming packets.
* @author Kristian
*/
public static class Client extends ObjectEnum<PacketType> {
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 TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x01, 0x14, "TabComplete");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x02, 0x01, "Chat");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x03, 0x16, "ClientCommand");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x04, 0x15, "Settings");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x05, 0x0F, "Transaction");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x06, 0x11, "EnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x07, 0x0E, "WindowClick");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x08, 0x0D, "CloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x09, 0x17, "CustomPayload");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0A, 0x02, "UseEntity");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x0B, 0x00, "KeepAlive");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x0C, 0x04, "Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x0D, 0x06, "PositionLook");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x0E, 0x05, "Look");
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 0x0F, 0x03, "Flying");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x10, 0xF8, "VehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x11, 0xF7, "BoatMove");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x12, 0x13, "Abilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x13, 0x07, "BlockDig");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x14, 0x0B, "EntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x15, 0x0C, "SteerVehicle");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x16, 0x19, "ResourcePackStatus");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x17, 0x09, "HeldItemSlot");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x18, 0x10, "SetCreativeSlot");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x19, 0x12, "UpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x1A, 0x0A, "ArmAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x1B, 0x18, "Spectate");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x1C, 0xF6, "UseItem");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x1D, 0x08, "BlockPlace");
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");
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(PacketType.class); }
private Client() { super(); }
public static Sender getSender() {
return SENDER;
@@ -287,22 +327,24 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Outgoing packets.
* @author Kristian
*/
public static class Server extends ObjectEnum<PacketType> {
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.SERVER;
public static final PacketType SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "ServerInfo").forceAsync(true);
@ForceAsync
public static final PacketType SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "ServerInfo");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "Pong");
/**
* @deprecated Replaced by {@link SERVER_INFO}
* @deprecated Renamed to {@link #SERVER_INFO}
*/
@Deprecated
public static final PacketType OUT_SERVER_INFO = SERVER_INFO;
@ForceAsync
public static final PacketType OUT_SERVER_INFO = SERVER_INFO.clone();
private final static Server INSTANCE = new Server();
// Prevent accidental construction
private Server() { super(PacketType.class); }
private Server() { super(); }
public static Sender getSender() {
return SENDER;
@@ -316,7 +358,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Incoming packets.
* @author Kristian
*/
public static class Client extends ObjectEnum<PacketType> {
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "Start");
@@ -325,7 +367,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(PacketType.class); }
private Client() { super(); }
public static Sender getSender() {
return SENDER;
@@ -351,7 +393,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Outgoing packets.
* @author Kristian
*/
public static class Server extends ObjectEnum<PacketType> {
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.SERVER;
public static final PacketType DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "Disconnect");
@@ -362,7 +404,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
private final static Server INSTANCE = new Server();
// Prevent accidental construction
private Server() { super(PacketType.class); }
private Server() { super(); }
public static Sender getSender() {
return SENDER;
@@ -376,7 +418,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Incoming packets.
* @author Kristian
*/
public static class Client extends ObjectEnum<PacketType> {
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "Start");
@@ -385,7 +427,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(PacketType.class); }
private Client() { super(); }
public static Sender getSender() {
return SENDER;
@@ -412,7 +454,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @author Kristian
*/
// Missing server packets: [10, 11, 12, 21, 107, 252]
public static class Server extends ObjectEnum<PacketType> {
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.SERVER;
public static final PacketType PLAYER_FLYING = PacketType.newLegacy(SENDER, 10);
@@ -436,7 +478,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
// Prevent accidental construction
private Server() {
super(PacketType.class);
super();
}
public static Sender getSender() {
@@ -452,7 +494,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @author Kristian
*/
// Missing client packets: [1, 9, 255]
public static class Client extends ObjectEnum<PacketType> {
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType LOGIN = PacketType.newLegacy(SENDER, 1);
@@ -462,7 +504,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(PacketType.class); }
private Client() { super(); }
public static Sender getSender() {
return SENDER;
@@ -512,7 +554,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
}
public String getPacketName() {
return WordUtils.capitalize(name().toLowerCase());
return WordUtils.capitalize(name().toLowerCase(Locale.ENGLISH));
}
}
@@ -545,13 +587,20 @@ public class PacketType implements Serializable, Comparable<PacketType> {
}
}
/**
* Whether or not packets of this type must be handled asynchronously.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ForceAsync { }
// Lookup of packet types
private static PacketTypeLookup LOOKUP;
/**
* Protocol version of all the current IDs.
*/
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.BOUNTIFUL_UPDATE;
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.EXPLORATION_UPDATE;
private final Protocol protocol;
private final Sender sender;
@@ -560,7 +609,10 @@ public class PacketType implements Serializable, Comparable<PacketType> {
private final MinecraftVersion version;
private final String[] classNames;
private String name;
private boolean deprecated;
private boolean forceAsync;
private boolean dynamic;
/**
@@ -612,7 +664,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @param packetId - the legacy packet ID.
* @return The corresponding packet type.
* @throws IllegalArgumentException If the legacy packet could not be found.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public static PacketType findLegacy(int packetId) {
PacketType type = getLookup().getFromLegacy(packetId);
@@ -627,7 +681,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @param preference - the preferred sender, or NULL for any arbitrary sender.
* @return The corresponding packet type.
* @throws IllegalArgumentException If the legacy packet could not be found.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public static PacketType findLegacy(int packetId, Sender preference) {
if (preference == null)
return findLegacy(packetId);
@@ -642,7 +698,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Determine if the given legacy packet exists.
* @param packetId - the legacy packet ID.
* @return TRUE if it does, FALSE otherwise.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public static boolean hasLegacy(int packetId) {
return getLookup().getFromLegacy(packetId) != null;
}
@@ -650,18 +708,19 @@ public class PacketType implements Serializable, Comparable<PacketType> {
/**
* Retrieve a packet type from a protocol, sender and packet ID.
* <p>
* It is usually better to access the packet types statically, like so:
* It is almost always better to access the packet types statically, like so:
* <ul>
* <li>{@link PacketType.Play.Server#SPAWN_ENTITY}
* </ul>
* However there are some valid uses for packet IDs. Please note that IDs
* change almost every Minecraft version.
*
* @param protocol - the current protocol.
* @param sender - the sender.
* @param packetId - the packet ID.
* @return The corresponding packet type.
* @throws IllegalArgumentException If the current packet could not be found.
* @deprecated IDs are no longer reliable
*/
@Deprecated
public static PacketType findCurrent(Protocol protocol, Sender sender, int packetId) {
PacketType type = getLookup().getFromCurrent(protocol, sender, packetId);
@@ -687,7 +746,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
if (name.contains("Packet"))
return name;
return String.format("Packet%s%s%s", protocol.getPacketName(), sender.getPacketName(), name);
return String.format("Packet%s%s%s", protocol.getPacketName(), sender.getPacketName(), WordUtils.capitalize(name));
}
/**
@@ -696,9 +755,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @param sender - the sender.
* @param packetId - the packet ID.
* @return TRUE if it exists, FALSE otherwise.
* @deprecated IDs are no longer reliable
*/
@Deprecated
public static boolean hasCurrent(Protocol protocol, Sender sender, int packetId) {
return getLookup().getFromCurrent(protocol, sender, packetId) != null;
}
@@ -711,7 +768,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @param sender - the sender of the packet, or NULL if unknown.
* @return The packet type.
* @throws IllegalArgumentException If the sender is NULL and the packet doesn't exist.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public static PacketType fromLegacy(int id, Sender sender) {
PacketType type = getLookup().getFromLegacy(id, sender);
@@ -765,24 +824,11 @@ public class PacketType implements Serializable, Comparable<PacketType> {
Map<String, PacketType> map = lookup.getMap(protocol, sender);
// Check the map first
String className = packetClass.getSimpleName();
PacketType type = map.get(className);
String clazz = packetClass.getSimpleName();
PacketType type = find(map, clazz);
if (type == null) {
// Then check any aliases
for (PacketType check : map.values()) {
String[] aliases = check.getClassNames();
if (aliases.length > 1) {
for (String alias : aliases) {
if (alias.equals(className)) {
// We have a match!
type = check;
}
}
}
}
// Guess we don't support this packet :/
type = new PacketType(protocol, sender, packetId, -1, PROTOCOL_VERSION, className);
type = new PacketType(protocol, sender, packetId, -1, PROTOCOL_VERSION, clazz);
type.dynamic = true;
// Many may be scheduled, but only the first will be executed
@@ -792,6 +838,28 @@ public class PacketType implements Serializable, Comparable<PacketType> {
return type;
}
private static PacketType find(Map<String, PacketType> map, String clazz) {
PacketType ret = map.get(clazz);
if (ret != null) {
return ret;
}
// Check any aliases
for (PacketType check : map.values()) {
String[] aliases = check.getClassNames();
if (aliases.length > 1) {
for (String alias : aliases) {
if (alias.equals(clazz)) {
// We have a match!
return check;
}
}
}
}
return null;
}
/**
* Lookup a packet type from a packet class.
* @param packetClass - the packet class.
@@ -837,7 +905,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
Callable<Boolean> callable = new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
ObjectEnum<PacketType> objEnum;
PacketTypeEnum objEnum;
// A bit ugly, but performance is critical
objEnum = getObjectEnum(type);
@@ -866,7 +934,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @param type - the packet type.
* @return The corresponding object enum.
*/
public static ObjectEnum<PacketType> getObjectEnum(final PacketType type) {
public static PacketTypeEnum getObjectEnum(final PacketType type) {
switch (type.getProtocol()) {
case HANDSHAKING:
return type.isClient() ? Handshake.Client.getInstance() : Handshake.Server.getInstance();
@@ -913,7 +981,6 @@ public class PacketType implements Serializable, Comparable<PacketType> {
for (int i = 0; i < classNames.length; i++) {
classNames[i] = format(protocol, sender, names[i]);
}
//System.out.println(Arrays.toString(classNames));
}
/**
@@ -971,11 +1038,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* <p>
* This is only unique within a specific protocol and target.
* <p>
* It is only unknown if the packet was removed in Minecraft 1.7.2.
* It is unknown if the packet was removed at any point.
* @return The current ID, or {@link #UNKNOWN_PACKET} if unknown.
* @deprecated IDs are subject to change
*/
@Deprecated
public int getCurrentId() {
return currentId;
}
@@ -996,12 +1061,47 @@ public class PacketType implements Serializable, Comparable<PacketType> {
}
}
// Only used by Enum processor
void setName(String name) {
this.name = name;
}
/**
* Retrieve the declared enum name of this packet type.
* @return The enum name.
*/
public String name() {
return getObjectEnum(this).getDeclaredName(this);
return name;
}
// Only used by enum processor
void setDeprecated() {
this.deprecated = true;
}
/**
* Whether or not this packet is deprecated. Deprecated packet types have either been renamed, replaced, or removed.
* Kind of like the thing they use to tell children to recycle except with packets you probably shouldn't be using.
*
* @return True if the type is deprecated, false if not
*/
public boolean isDeprecated() {
return deprecated;
}
// Only used by enum processor
void forceAsync() {
this.forceAsync = true;
}
/**
* Whether or not the processing of this packet must take place on a thread different than the main thread. You don't
* get a choice. If this is false it's up to you.
*
* @return True if async processing is forced, false if not.
*/
public boolean isAsyncForced() {
return forceAsync;
}
/**
@@ -1017,7 +1117,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* <p>
* This ID is globally unique.
* @return The legacy ID, or {@link #UNKNOWN_PACKET} if unknown.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public int getLegacyId() {
return legacyId;
}
@@ -1030,22 +1132,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
return dynamic;
}
private PacketType forceAsync(boolean forceAsync) {
this.forceAsync = forceAsync;
return this;
}
/**
* Whether or not this packet must be processed asynchronously.
* @return True if it must be, false if not.
*/
public boolean forceAsync() {
return forceAsync;
}
@Override
public int hashCode() {
return Objects.hashCode(protocol, sender, currentId, legacyId);
return Objects.hashCode(protocol, sender, currentId);
}
@Override
@@ -1057,8 +1146,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
PacketType other = (PacketType) obj;
return protocol == other.protocol &&
sender == other.sender &&
currentId == other.currentId &&
legacyId == other.legacyId;
currentId == other.currentId;
}
return false;
}
@@ -1069,7 +1157,6 @@ public class PacketType implements Serializable, Comparable<PacketType> {
compare(protocol, other.getProtocol()).
compare(sender, other.getSender()).
compare(currentId, other.getCurrentId()).
compare(legacyId, other.getLegacyId()).
result();
}
@@ -1078,8 +1165,17 @@ public class PacketType implements Serializable, Comparable<PacketType> {
Class<?> clazz = getPacketClass();
if (clazz == null)
return "UNREGISTERED[" + protocol + ", " + sender + ", " + currentId + ", legacy: " + legacyId + ", classNames: " + Arrays.toString(classNames) + "]";
return name() + "[" + protocol + ", " + sender + ", " + currentId + ", classNames: " + Arrays.toString(classNames) + " (unregistered)]";
else
return clazz.getSimpleName() + "[" + currentId + ", legacy: " + legacyId + "]";
return name() + "[class=" + clazz.getSimpleName() + ", id=" + currentId + "]";
}
@Override
public PacketType clone() {
try {
return (PacketType) super.clone();
} catch (CloneNotSupportedException ex) {
throw new Error("This shouldn't happen", ex);
}
}
}
@@ -15,7 +15,7 @@
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect;
package com.comphenix.protocol;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -23,8 +23,10 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import com.comphenix.protocol.PacketType;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Sets;
/**
* Represents a more modern object-based enum.
@@ -33,41 +35,53 @@ import com.google.common.collect.HashBiMap;
* want to prevent the creation of additional members dynamically.
* @author Kristian
*/
public class ObjectEnum<T> implements Iterable<T> {
public class PacketTypeEnum implements Iterable<PacketType> {
// Used to convert between IDs and names
protected BiMap<T, String> members = HashBiMap.create();
protected Set<PacketType> members = Sets.newHashSet();
/**
* Registers every declared integer field.
* @param fieldType Field type
* Registers every declared PacketType field.
*/
public ObjectEnum(Class<T> fieldType) {
registerAll(fieldType);
public PacketTypeEnum() {
registerAll();
}
/**
* Registers every public assignable static field as a member.
* @param fieldType Field type
*/
@SuppressWarnings("unchecked")
protected void registerAll(Class<T> fieldType) {
protected void registerAll() {
try {
// Register every int field
// Register every non-deprecated field
for (Field entry : this.getClass().getFields()) {
if (Modifier.isStatic(entry.getModifiers()) && fieldType.isAssignableFrom(entry.getType())) {
T value = (T) entry.get(null);
if (value == null)
throw new IllegalArgumentException("Field " + entry + " was NULL. Remember to " +
"construct the object after the field has been declared.");
registerMember(value, entry.getName());
if (Modifier.isStatic(entry.getModifiers()) && PacketType.class.isAssignableFrom(entry.getType())) {
PacketType value = (PacketType) entry.get(null);
if (value == null) {
throw new IllegalArgumentException("Field " + entry.getName() + " was null!");
}
value.setName(entry.getName());
if (entry.getAnnotation(PacketType.ForceAsync.class) != null) {
value.forceAsync();
}
boolean deprecated = entry.getAnnotation(Deprecated.class) != null;
if (deprecated) value.setDeprecated();
if (members.contains(value)) {
// Replace potentially deprecated packet types with non-deprecated ones
if (!deprecated) {
members.remove(value);
members.add(value);
}
} else {
members.add(value);
}
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
@@ -77,11 +91,14 @@ public class ObjectEnum<T> implements Iterable<T> {
* @param name - name of member.
* @return TRUE if the member was registered, FALSE otherwise.
*/
public boolean registerMember(T instance, String name) {
if (!members.containsKey(instance)) {
members.put(instance, name);
public boolean registerMember(PacketType instance, String name) {
instance.setName(name);
if (!members.contains(instance)) {
members.add(instance);
return true;
}
return false;
}
@@ -90,38 +107,36 @@ public class ObjectEnum<T> implements Iterable<T> {
* @param member - the member to check.
* @return TRUE if the given member has been registered, FALSE otherwise.
*/
public boolean hasMember(T member) {
return members.containsKey(member);
public boolean hasMember(PacketType member) {
return members.contains(member);
}
/**
* Retrieve a member by name,
* @param name - name of member to retrieve.
* @return The member, or NULL if not found.
* @deprecated Don't use this
*/
public T valueOf(String name) {
return members.inverse().get(name);
@Deprecated
public PacketType valueOf(String name) {
for (PacketType member : members) {
if (member.name().equals(name))
return member;
}
return null;
}
/**
* Retrieve the name of the given member.
* @param member - the member to retrieve.
* @return Declared name of the member, or NULL if not found.
*/
public String getDeclaredName(T member) {
return members.get(member);
}
/**
* Retrieve every registered member.
* @return Enumeration of every value.
*/
public Set<T> values() {
return new HashSet<T>(members.keySet());
public Set<PacketType> values() {
return new HashSet<>(members);
}
@Override
public Iterator<T> iterator() {
return members.keySet().iterator();
public Iterator<PacketType> iterator() {
return members.iterator();
}
}
@@ -20,6 +20,7 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
@@ -464,7 +465,7 @@ public class ProtocolConfig {
PlayerInjectHooks hook = getDefaultMethod();
if (text != null)
hook = PlayerInjectHooks.valueOf(text.toUpperCase().replace(" ", "_"));
hook = PlayerInjectHooks.valueOf(text.toUpperCase(Locale.ENGLISH).replace(" ", "_"));
return hook;
}
@@ -34,17 +34,17 @@ public class ProtocolLibrary {
/**
* The minimum version ProtocolLib has been tested with.
*/
public static final String MINIMUM_MINECRAFT_VERSION = "1.9";
public static final String MINIMUM_MINECRAFT_VERSION = "1.8";
/**
* The maximum version ProtocolLib has been tested with.
*/
public static final String MAXIMUM_MINECRAFT_VERSION = "1.9.4";
public static final String MAXIMUM_MINECRAFT_VERSION = "1.12";
/**
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.9.4) was released.
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.12) was released.
*/
public static final String MINECRAFT_LAST_RELEASE_DATE = "2016-05-10";
public static final String MINECRAFT_LAST_RELEASE_DATE = "2017-06-07";
/**
* Plugins that are currently incompatible with ProtocolLib.
@@ -73,4 +73,10 @@ public class ProtocolLogger {
log("[Debug] " + message, args);
}
}
public static void debug(String message, Throwable ex) {
if (isDebugEnabled()) {
plugin.getLogger().log(Level.WARNING, "[Debug] " + message, ex);
}
}
}
@@ -24,8 +24,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
@@ -36,10 +34,11 @@ import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.timing.TimedListenerManager;
import com.comphenix.protocol.timing.TimedListenerManager.ListenerType;
import com.comphenix.protocol.timing.TimedTracker;
import com.comphenix.protocol.utility.WrappedScheduler;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import org.bukkit.plugin.Plugin;
/**
* Represents a handler for an asynchronous event.
* <p>
@@ -345,8 +344,7 @@ public class AsyncListenerHandler {
}
private void scheduleAsync(Runnable runnable) {
// Handle deprecation
WrappedScheduler.runAsynchronouslyRepeat(listener.getPlugin(), filterManager.getScheduler(), runnable, 0L, -1L);
listener.getPlugin().getServer().getScheduler().runTaskAsynchronously(listener.getPlugin(), runnable);
}
/**
@@ -238,12 +238,7 @@ public class ListeningWhitelist {
if (this == EMPTY_WHITELIST)
return "EMPTY_WHITELIST";
else
return Objects.toStringHelper(this).
add("priority", priority).
add("packets", types).
add("gamephase", gamePhase).
add("options", options).
toString();
return "ListeningWhitelist[priority=" + priority + ", packets=" + types + ", gamephase=" + gamePhase + ", options=" + options + "]";
}
/**
@@ -17,9 +17,6 @@
package com.comphenix.protocol.events;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.UnpooledByteBufAllocator;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
@@ -36,6 +33,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nonnull;
@@ -76,10 +74,12 @@ 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;
@@ -105,11 +105,16 @@ import com.comphenix.protocol.wrappers.WrappedServerPing;
import com.comphenix.protocol.wrappers.WrappedStatistic;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.UnpooledByteBufAllocator;
/**
* Represents a Minecraft packet indirectly.
*
@@ -330,7 +335,15 @@ public class PacketContainer implements Serializable {
public StructureModifier<String> getStrings() {
return structureModifier.withType(String.class);
}
/**
* Retrieves a read/write structure for every UUID field.
* @return A modifier for every UUID field.
*/
public StructureModifier<UUID> getUUIDs() {
return structureModifier.withType(UUID.class);
}
/**
* Retrieves a read/write structure for every String array field.
* @return A modifier for every String array field.
@@ -366,7 +379,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for ItemStack.
* <p>
* This modifier will automatically marshall between the Bukkit ItemStack and the
* This modifier will automatically marshal between the Bukkit ItemStack and the
* internal Minecraft ItemStack.
* @return A modifier for ItemStack fields.
*/
@@ -379,7 +392,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for arrays of ItemStacks.
* <p>
* This modifier will automatically marshall between the Bukkit ItemStack and the
* This modifier will automatically marshal between the Bukkit ItemStack and the
* internal Minecraft ItemStack.
* @return A modifier for ItemStack array fields.
*/
@@ -389,7 +402,25 @@ public class PacketContainer implements Serializable {
MinecraftReflection.getItemStackArrayClass(),
BukkitConverters.getIgnoreNull(new ItemStackArrayConverter()));
}
/**
* Retrieves a read/write structure for lists of ItemStacks.
* <p>
* This modifier will automatically marshal between the Bukkit ItemStack and the
* internal Minecraft ItemStack.
* @return A modifier for ItemStack list fields.
*/
public StructureModifier<List<ItemStack>> getItemListModifier() {
// Convert to and from the Bukkit wrapper
return structureModifier.withType(
List.class,
BukkitConverters.getListConverter(
MinecraftReflection.getItemStackClass(),
BukkitConverters.getItemStackConverter()
)
);
}
/**
* Retrieve a read/write structure for maps of statistics.
* <p>
@@ -408,7 +439,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for the world type enum.
* <p>
* This modifier will automatically marshall between the Bukkit world type and the
* This modifier will automatically marshal between the Bukkit world type and the
* internal Minecraft world type.
* @return A modifier for world type fields.
*/
@@ -508,6 +539,21 @@ public class PacketContainer implements Serializable {
BukkitConverters.getNbtConverter());
}
/**
* Retrieves a read/write structure for lists of NBT classes.
* @return A modifier for lists of NBT classes.
*/
public StructureModifier<List<NbtBase<?>>> getListNbtModifier() {
// Convert to and from the ProtocolLib wrapper
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(
MinecraftReflection.getNBTBaseClass(),
BukkitConverters.getNbtConverter()
)
);
}
/**
* Retrieves a read/write structure for Vectors.
* @return A modifier for Vectors.
@@ -522,7 +568,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for collections of attribute snapshots.
* <p>
* This modifier will automatically marshall between the visible ProtocolLib WrappedAttribute and the
* This modifier will automatically marshal between the visible ProtocolLib WrappedAttribute and the
* internal Minecraft AttributeSnapshot.
* @return A modifier for AttributeSnapshot collection fields.
*/
@@ -539,7 +585,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for collections of chunk positions.
* <p>
* This modifier will automatically marshall between the visible ProtocolLib ChunkPosition and the
* This modifier will automatically marshal between the visible ProtocolLib ChunkPosition and the
* internal Minecraft ChunkPosition.
*
* @return A modifier for ChunkPosition list fields.
@@ -557,7 +603,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for collections of chunk positions.
* <p>
* This modifier will automatically marshall between the visible ProtocolLib BlockPosition and the
* This modifier will automatically marshal between the visible ProtocolLib BlockPosition and the
* internal Minecraft BlockPosition.
*
* @return A modifier for ChunkPosition list fields.
@@ -575,7 +621,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for collections of watchable objects.
* <p>
* This modifier will automatically marshall between the visible WrappedWatchableObject and the
* This modifier will automatically marshal between the visible WrappedWatchableObject and the
* internal Minecraft WatchableObject.
* @return A modifier for watchable object list fields.
*/
@@ -592,7 +638,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for block fields.
* <p>
* This modifier will automatically marshall between Material and the
* This modifier will automatically marshal between Material and the
* internal Minecraft Block.
* @return A modifier for GameProfile fields.
*/
@@ -605,7 +651,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for game profiles in Minecraft 1.7.2.
* <p>
* This modifier will automatically marshall between WrappedGameProfile and the
* This modifier will automatically marshal between WrappedGameProfile and the
* internal Minecraft GameProfile.
* @return A modifier for GameProfile fields.
*/
@@ -618,7 +664,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for BlockData in Minecraft 1.8.
* <p>
* This modifier will automatically marshall between WrappedBlockData and the
* This modifier will automatically marshal between WrappedBlockData and the
* internal Minecraft IBlockData.
* @return A modifier for BlockData fields.
*/
@@ -631,7 +677,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for MultiBlockChangeInfo arrays in Minecraft 1.8.
* <p>
* This modifier will automatically marshall between MultiBlockChangeInfo and the
* This modifier will automatically marshal between MultiBlockChangeInfo and the
* internal Minecraft MultiBlockChangeInfo.
* @return A modifier for BlockData fields.
*/
@@ -646,7 +692,7 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for chat components in Minecraft 1.7.2.
* <p>
* This modifier will automatically marshall between WrappedChatComponent and the
* This modifier will automatically marshal between WrappedChatComponent and the
* internal Minecraft IChatBaseComponent.
* @return A modifier for ChatComponent fields.
*/
@@ -659,15 +705,19 @@ public class PacketContainer implements Serializable {
/**
* Retrieves a read/write structure for arrays of chat components.
* <p>
* This modifier will automatically marshall between WrappedChatComponent and the
* internal Minecraft IChatBaseComponent.
* This modifier will automatically marshal between WrappedChatComponent and the
* internal Minecraft IChatBaseComponent (1.9.2 and below) or the internal
* NBTCompound (1.9.4 and up).
* <p>
* Note that in 1.9.4 and up this modifier only works properly with sign
* tile entities.
* @return A modifier for ChatComponent array fields.
*/
public StructureModifier<WrappedChatComponent[]> getChatComponentArrays() {
// Convert to and from the Bukkit wrapper
return structureModifier.<WrappedChatComponent[]>withType(
MinecraftReflection.getIChatBaseComponentArrayClass(),
BukkitConverters.getIgnoreNull(new WrappedChatComponentArrayConverter()));
ComponentArrayConverter.getGenericType(),
BukkitConverters.getIgnoreNull(new ComponentArrayConverter()));
}
/**
@@ -881,7 +931,7 @@ public class PacketContainer implements Serializable {
}
/**
* Retrive a read/write structure for the ItemSlot enum in 1.9.
* Retrieve a read/write structure for the ItemSlot enum in 1.9.
* @return A modifier for ItemSlot enum fields.
*/
public StructureModifier<ItemSlot> getItemSlots() {
@@ -890,7 +940,7 @@ public class PacketContainer implements Serializable {
}
/**
* Retrive a read/write structure for the Hand enum in 1.9.
* Retrieve a read/write structure for the Hand enum in 1.9.
* @return A modifier for Hand enum fields.
*/
public StructureModifier<Hand> getHands() {
@@ -898,6 +948,24 @@ public class PacketContainer implements Serializable {
EnumWrappers.getHandClass(), EnumWrappers.getHandConverter());
}
/**
* Retrieve a read/write structure for the Direction enum in 1.10.
* @return A modifier for Direction enum fields.
*/
public StructureModifier<Direction> getDirections() {
return structureModifier.<Direction>withType(
EnumWrappers.getDirectionClass(), EnumWrappers.getDirectionConverter());
}
/**
* Retrieve a read/write structure for the ChatType enum in 1.12.
* @return A modifier for ChatType enum fields.
*/
public StructureModifier<ChatType> getChatTypes() {
return structureModifier.<ChatType>withType(
EnumWrappers.getChatTypeClass(), EnumWrappers.getChatTypeConverter());
}
/**
* Retrieve a read/write structure for an enum. This allows for the use of
* user-created enums that may not exist in ProtocolLib. The specific (user
@@ -923,7 +991,7 @@ public class PacketContainer implements Serializable {
*/
public <T extends Enum<T>> StructureModifier<T> getEnumModifier(Class<T> enumClass, int index) {
return getEnumModifier(enumClass, structureModifier.getField(index).getType());
}
}
/**
* Retrieves the ID of this packet.
@@ -1076,7 +1144,7 @@ public class PacketContainer implements Serializable {
* Construct a new packet data serializer.
* @return The packet data serializer.
*/
private ByteBuf createPacketBuffer() {
public static ByteBuf createPacketBuffer() {
ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.buffer();
Class<?> packetSerializer = MinecraftReflection.getPacketDataSerializerClass();
@@ -1224,11 +1292,11 @@ public class PacketContainer implements Serializable {
* Represents an equivalent converter for ChatComponent arrays.
* @author dmulloy2
*/
private static class WrappedChatComponentArrayConverter implements EquivalentConverter<WrappedChatComponent[]> {
private static class LegacyComponentConverter implements EquivalentConverter<WrappedChatComponent[]> {
final EquivalentConverter<WrappedChatComponent> componentConverter = BukkitConverters.getWrappedChatComponentConverter();
@Override
public Object getGeneric(Class<?>genericType, WrappedChatComponent[] specific) {
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
Class<?> nmsComponent = MinecraftReflection.getIChatBaseComponentClass();
Object[] result = (Object[]) Array.newInstance(nmsComponent, specific.length);
@@ -1257,6 +1325,93 @@ public class PacketContainer implements Serializable {
}
}
/**
* Converts from NBT to WrappedChatComponent arrays
* @author dmulloy2
*/
private static class NBTComponentConverter implements EquivalentConverter<WrappedChatComponent[]> {
private EquivalentConverter<NbtBase<?>> nbtConverter = BukkitConverters.getNbtConverter();
private final int lines = 4;
@Override
public WrappedChatComponent[] getSpecific(Object generic) {
NbtBase<?> nbtBase = nbtConverter.getSpecific(generic);
NbtCompound compound = (NbtCompound) nbtBase;
WrappedChatComponent[] components = new WrappedChatComponent[lines];
for (int i = 0; i < lines; i++) {
if (compound.containsKey("Text" + (i + 1))) {
components[i] = WrappedChatComponent.fromJson(compound.getString("Text" + (i + 1)));
} else {
components[i] = WrappedChatComponent.fromText("");
}
}
return components;
}
@Override
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
NbtCompound compound = NbtFactory.ofCompound("");
for (int i = 0; i < lines; i++) {
WrappedChatComponent component;
if (i < specific.length && specific[i] != null) {
component = specific[i];
} else {
component = WrappedChatComponent.fromText("");
}
compound.put("Text" + (i + 1), component.getJson());
}
return nbtConverter.getGeneric(genericType, compound);
}
@Override
public Class<WrappedChatComponent[]> getSpecificType() {
return WrappedChatComponent[].class;
}
}
/**
* A delegated converter that supports NBT to Component Array and regular Component Array
* @author dmulloy2
*/
private static class ComponentArrayConverter implements EquivalentConverter<WrappedChatComponent[]> {
private static final EquivalentConverter<WrappedChatComponent[]> DELEGATE;
static {
if (!PacketType.Play.Server.UPDATE_SIGN.isDeprecated()) {
DELEGATE = new LegacyComponentConverter();
} else {
DELEGATE = new NBTComponentConverter();
}
}
@Override
public WrappedChatComponent[] getSpecific(Object generic) {
return DELEGATE.getSpecific(generic);
}
@Override
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
return DELEGATE.getGeneric(genericType, specific);
}
@Override
public Class<WrappedChatComponent[]> getSpecificType() {
return DELEGATE.getSpecificType();
}
public static Class<?> getGenericType() {
if (DELEGATE instanceof NBTComponentConverter) {
return MinecraftReflection.getNBTCompoundClass();
} else {
return MinecraftReflection.getIChatBaseComponentArrayClass();
}
}
}
@Override
public String toString() {
return "PacketContainer[type=" + type + ", structureModifier=" + structureModifier + "]";
@@ -7,7 +7,6 @@ import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketStream;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.ProtocolLibrary;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
/**
@@ -138,10 +137,6 @@ public class ScheduledPacket {
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("packet", packet)
.add("target", target)
.add("filtered", filtered)
.toString();
return "ScheduledPacket[packet=" + packet + ", target=" + target + ", filtered=" + filtered + "]";
}
}
@@ -139,7 +139,6 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return banned;
}
@Override
public void setBanned(boolean banned) {
this.banned = banned;
}
@@ -24,6 +24,9 @@ import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
@@ -31,6 +34,7 @@ import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.PacketConstructor.Unwrapper;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.primitives.Primitives;
/**
@@ -182,20 +186,55 @@ public class BukkitUnwrapper implements Unwrapper {
Report.newBuilder(REPORT_SECURITY_LIMITATION).error(e).callerParam(type)
);
} catch (NoSuchMethodException e) {
// Maybe it's a proxy?
Unwrapper proxyUnwrapper = getProxyUnwrapper(type);
if (proxyUnwrapper != null)
return proxyUnwrapper;
// Try getting the field unwrapper too
Unwrapper fieldUnwrapper = getFieldUnwrapper(type);
if (fieldUnwrapper != null)
return fieldUnwrapper;
else
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_FIND_UNWRAP_METHOD).error(e).callerParam(type));
}
// Default method
return null;
}
// Players should /always/ be able to be unwrapped
// We should only get here if the 'Player' is a proxy
private Unwrapper getProxyUnwrapper(final Class<?> type) {
try {
if (Player.class.isAssignableFrom(type)) {
final Method getHandle = MinecraftReflection.getCraftPlayerClass().getMethod("getHandle");
Unwrapper unwrapper = new Unwrapper() {
@Override
public Object unwrapItem(Object wrapped) {
try {
return getHandle.invoke(((Player) wrapped).getPlayer());
} catch (Throwable ex) {
try {
return getHandle.invoke(Bukkit.getPlayer(((Player) wrapped).getUniqueId()));
} catch (ReflectiveOperationException ex1) {
throw new RuntimeException("Failed to unwrap proxy " + wrapped, ex);
}
}
}
};
unwrapperCache.put(type, unwrapper);
return unwrapper;
}
} catch (Throwable ignored) {
}
return null;
}
/**
* Retrieve a cached unwrapper using the handle field.
* @param type - a cached field unwrapper.
@@ -67,9 +67,11 @@ public class StructureCache {
// TODO: Optimize DefaultInstances
Object result = DefaultInstances.DEFAULT.create(clazz);
if (result != null) {
return result;
if (result == null) {
throw new IllegalArgumentException("Failed to create packet for type: " + type);
}
return result;
}
throw new IllegalArgumentException("Cannot find associated packet class: " + type);
}
@@ -28,6 +28,7 @@ import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.channels.WritableByteChannel;
@@ -44,6 +45,7 @@ import com.google.common.io.ByteStreams;
* all indexing in the byte buffer.
* @author Kristian
*/
@SuppressWarnings("unused")
public class NettyByteBufAdapter extends AbstractByteBuf {
private DataInputStream input;
private DataOutputStream output;
@@ -214,7 +216,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
@Override
public ByteBufAllocator alloc() {
return null;
return ByteBufAllocator.DEFAULT;
}
@Override
@@ -389,4 +391,48 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
public ByteBuf retain() {
return this;
}
protected int _getIntLE(int arg0) {
return 0;
}
protected long _getLongLE(int arg0) {
return 0;
}
protected short _getShortLE(int arg0) {
return 0;
}
protected int _getUnsignedMediumLE(int arg0) {
return 0;
}
protected void _setIntLE(int arg0, int arg1) {
}
protected void _setLongLE(int arg0, long arg1) {
}
protected void _setMediumLE(int arg0, int arg1) {
}
protected void _setShortLE(int arg0, int arg1) {
}
public int getBytes(int arg0, FileChannel arg1, long arg2, int arg3) throws IOException {
return 0;
}
public int setBytes(int arg0, FileChannel arg1, long arg2, int arg3) throws IOException {
return 0;
}
public ByteBuf touch() {
return null;
}
public ByteBuf touch(Object arg0) {
return null;
}
}
@@ -40,8 +40,6 @@ public class NettyProtocolRegistry extends ProtocolRegistry {
@Override
protected synchronized void initialize() {
ProtocolLogger.debug("Initializing the Netty protocol registry"); // Debug for issue #202
Object[] protocols = enumProtocol.getEnumConstants();
// ID to Packet class maps
@@ -75,8 +73,7 @@ public class NettyProtocolRegistry extends ProtocolRegistry {
result.containers.add(new MapContainer(map));
}
for (int i = 0; i < protocols.length; i++) {
Object protocol = protocols[i];
for (Object protocol : protocols) {
Enum<?> enumProtocol = (Enum<?>) protocol;
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
@@ -103,8 +100,8 @@ public class NettyProtocolRegistry extends ProtocolRegistry {
register.serverPackets.add(type);
if (sender == Sender.CLIENT)
register.clientPackets.add(type);
} catch (IllegalArgumentException ex) {
// Sometimes this happens with fake packets, just ignore it
} catch (Exception ex) {
ProtocolLogger.debug("Encountered an exception associating packet " + type, ex);
}
}
}
@@ -16,31 +16,216 @@
*/
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) {
int i = id;
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;
@@ -49,7 +234,22 @@ public class WirePacket {
output.writeByte(i);
}
public void writeBytes(ByteBuf output) {
output.writeBytes(bytes);
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;
}
}
@@ -18,6 +18,7 @@
package com.comphenix.protocol.injector.packet;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -28,6 +29,7 @@ import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.netty.NettyProtocolRegistry;
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -154,11 +156,10 @@ public class PacketRegistry {
*/
@Deprecated
public static Set<Integer> getServerPackets() throws FieldAccessException {
initialize();
if (LEGACY_SERVER_PACKETS == null) {
LEGACY_SERVER_PACKETS = toLegacy(NETTY.getServerPackets());
LEGACY_SERVER_PACKETS = toLegacy(getServerPacketTypes());
}
return LEGACY_SERVER_PACKETS;
}
@@ -168,8 +169,8 @@ public class PacketRegistry {
*/
public static Set<PacketType> getServerPacketTypes() {
initialize();
NETTY.synchronize();
return NETTY.getServerPackets();
}
@@ -182,11 +183,10 @@ public class PacketRegistry {
*/
@Deprecated
public static Set<Integer> getClientPackets() throws FieldAccessException {
initialize();
if (LEGACY_CLIENT_PACKETS == null) {
LEGACY_CLIENT_PACKETS = toLegacy(NETTY.getClientPackets());
LEGACY_CLIENT_PACKETS = toLegacy(getClientPacketTypes());
}
return LEGACY_CLIENT_PACKETS;
}
@@ -196,8 +196,8 @@ public class PacketRegistry {
*/
public static Set<PacketType> getClientPacketTypes() {
initialize();
NETTY.synchronize();
return NETTY.getClientPackets();
}
@@ -269,7 +269,24 @@ public class PacketRegistry {
*/
public static Class getPacketClassFromType(PacketType type, boolean forceVanilla) {
initialize();
return NETTY.getPacketTypeLookup().get(type);
// Try the lookup first
Class<?> clazz = NETTY.getPacketTypeLookup().get(type);
if (clazz != null) {
return clazz;
}
// Then try looking up the class names
for (String name : type.getClassNames()) {
try {
clazz = MinecraftReflection.getMinecraftClass(name);
break;
} catch (Exception ex) {
}
}
// TODO Cache the result?
return clazz;
}
/**
@@ -17,10 +17,7 @@
package com.comphenix.protocol.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
@@ -421,6 +418,30 @@ public class FuzzyReflection {
return fields;
}
/**
* Retrieves a field with a given type and parameters. This is most useful
* when dealing with Collections.
*
* @param fieldType Type of the field
* @param params Variable length array of type parameters
* @return The field
*
* @throws IllegalArgumentException If the field cannot be found
*/
public Field getParameterizedField(Class<?> fieldType, Class<?>... params) {
for (Field field : getFields()) {
if (field.getType().equals(fieldType)) {
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
if (Arrays.equals(((ParameterizedType) type).getActualTypeArguments(), params))
return field;
}
}
}
throw new IllegalArgumentException("Unable to find a field with type " + fieldType + " and params " + Arrays.toString(params));
}
/**
* Retrieve the first field that matches.
@@ -17,7 +17,6 @@ import com.google.common.collect.Lists;
*
* @author Kristian
*/
@SuppressWarnings("unused") // Java 8 compat
public abstract class MethodInfo implements GenericDeclaration, Member {
/**
* Wraps a method as a MethodInfo object.
@@ -453,6 +453,17 @@ public class StructureModifier<TField> {
*/
@SuppressWarnings("unchecked")
public <T> StructureModifier<T> withType(Class fieldType, EquivalentConverter<T> converter) {
if (fieldType == null) {
// It's not supported in this version, so return an empty modifier
return new StructureModifier<T>() {
@Override
public T read(int index) { return null; }
@Override
public StructureModifier<T> write(int index, T value) { return this; }
};
}
StructureModifier<T> result = subtypeCache.get(fieldType);
// Do we need to update the cache?
@@ -462,7 +473,7 @@ public class StructureModifier<TField> {
int index = 0;
for (Field field : data) {
if (fieldType != null && fieldType.isAssignableFrom(field.getType())) {
if (fieldType.isAssignableFrom(field.getType())) {
filtered.add(field);
// Don't use the original index
@@ -476,14 +487,12 @@ public class StructureModifier<TField> {
// Cache structure modifiers
result = withFieldType(fieldType, filtered, defaults);
if (fieldType != null) {
subtypeCache.put(fieldType, result);
subtypeCache.put(fieldType, result);
// Automatically compile the structure modifier
if (useStructureCompiler && BackgroundCompiler.getInstance() != null)
BackgroundCompiler.getInstance().scheduleCompilation(subtypeCache, fieldType);
}
// Automatically compile the structure modifier
if (useStructureCompiler && BackgroundCompiler.getInstance() != null)
BackgroundCompiler.getInstance().scheduleCompilation(subtypeCache, fieldType);
}
// Add the target too
@@ -16,15 +16,20 @@
*/
package com.comphenix.protocol.reflect.cloning;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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.google.common.collect.Maps;
@@ -61,6 +66,16 @@ public class BukkitCloner implements Cloner {
addClass(5, MinecraftReflection.getDataWatcherSerializerClass());
addClass(6, MinecraftReflection.getMinecraftKeyClass());
}
try {
addClass(7, MinecraftReflection.getIBlockDataClass());
} catch (Throwable ex) {
}
try {
addClass(8, MinecraftReflection.getNonNullListClass());
} catch (Throwable ex) {
}
}
private void addClass(int id, Class<?> clazz) {
@@ -112,9 +127,47 @@ public class BukkitCloner implements Cloner {
return source;
case 6:
EquivalentConverter<MinecraftKey> keyConverter = MinecraftKey.getConverter();
return keyConverter.getGeneric(clonableClasses.get(5), keyConverter.getSpecific(source));
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());
}
}
private static Constructor<?> nonNullList = null;
private static final Cloner nonNullListCloner() {
return new Cloner() {
@Override
public boolean canClone(Object source) {
return MinecraftReflection.is(MinecraftReflection.getNonNullListClass(), source);
}
@Override
public Object clone(Object source) {
StructureModifier<Object> modifier = new StructureModifier<>(source.getClass(), true).withTarget(source);
List<?> list = (List<?>) modifier.read(0);
Object empty = modifier.read(1);
if (nonNullList == null) {
try {
nonNullList = source.getClass().getDeclaredConstructor(List.class, Object.class);
nonNullList.setAccessible(true);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Could not find NonNullList constructor", ex);
}
}
try {
return nonNullList.newInstance(new ArrayList<>(list), empty);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Could not create new NonNullList", ex);
}
}
};
}
}
@@ -1,24 +1,24 @@
/*
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* 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.
* 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
* 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.utility;
import java.util.Map;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
@@ -29,10 +29,10 @@ import com.google.common.collect.Maps;
* @author Kristian
*/
class CachedPackage {
private final Map<String, Class<?>> cache;
private final Map<String, Optional<Class<?>>> cache;
private final String packageName;
private final ClassSource source;
/**
* Construct a new cached package.
* @param packageName - the name of the current package.
@@ -43,16 +43,20 @@ class CachedPackage {
this.cache = Maps.newConcurrentMap();
this.source = source;
}
/**
* Associate a given class with a class name.
* @param className - class name.
* @param clazz - type of class.
*/
public void setPackageClass(String className, Class<?> clazz) {
cache.put(className, clazz);
if (clazz != null) {
cache.put(className, Optional.<Class<?>> of(clazz));
} else {
cache.remove(className);
}
}
/**
* Retrieve the class object of a specific class in the current package.
* @param className - the specific class.
@@ -60,25 +64,33 @@ class CachedPackage {
* @throws RuntimeException If we are unable to find the given class.
*/
public Class<?> getPackageClass(String className) {
try {
Class<?> result = cache.get(Preconditions.checkNotNull(className, "className cannot be NULL"));
// Concurrency is not a problem - we don't care if we look up a class twice
if (result == null) {
// Look up the class dynamically
result = source.loadClass(combine(packageName, className));
if (result == null)
throw new IllegalArgumentException("Source " + source + " returned NULL for " + className);
Preconditions.checkNotNull(className, "className cannot be null!");
cache.put(className, result);
// 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 result;
} catch (ClassNotFoundException e) {
throw new RuntimeException("Cannot find class " + combine(packageName, className), e);
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);
}
}
/**
* Correctly combine a package name and the child class we're looking for.
* @param packageName - name of the package, or an empty string for the default package.
@@ -21,7 +21,7 @@ package com.comphenix.protocol.utility;
*/
public final class Constants {
public static final String PACKAGE_VERSION = "v1_9_R2";
public static final String PACKAGE_VERSION = "v1_12_R1";
public static final String NMS = "net.minecraft.server." + PACKAGE_VERSION;
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
}
@@ -47,6 +47,7 @@ public class MinecraftFields {
* @return The player connection.
*/
public static Object getPlayerConnection(Player player) {
Preconditions.checkNotNull(player, "player cannot be null!");
return getPlayerConnection(BukkitUnwrapper.getInstance().unwrapItem(player));
}
@@ -16,6 +16,7 @@ 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;
/**
* Static methods for accessing Minecraft methods.
@@ -43,7 +44,13 @@ public class MinecraftMethods {
Class<?> serverHandlerClass = MinecraftReflection.getPlayerConnectionClass();
try {
sendPacketMethod = FuzzyReflection.fromClass(serverHandlerClass).getMethodByName("sendPacket.*");
sendPacketMethod = FuzzyReflection
.fromClass(serverHandlerClass)
.getMethod(FuzzyMethodContract.newBuilder()
.nameRegex("sendPacket.*")
.returnTypeVoid()
.parameterCount(1)
.build());
} catch (IllegalArgumentException e) {
// We can't use the method below on Netty
if (MinecraftReflection.isUsingNetty()) {
@@ -4,7 +4,6 @@ import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import com.comphenix.protocol.ProtocolLibrary;
import com.google.common.collect.Maps;
/**
@@ -42,6 +41,10 @@ public class MinecraftProtocolVersion {
map.put(new MinecraftVersion(1, 9, 0), 107);
map.put(new MinecraftVersion(1, 9, 2), 109);
map.put(new MinecraftVersion(1, 9, 4), 110);
map.put(new MinecraftVersion(1, 10, 0), 210);
map.put(new MinecraftVersion(1, 11, 0), 315);
map.put(new MinecraftVersion(1, 11, 1), 316);
map.put(new MinecraftVersion(1, 12, 0), 335);
return map;
}
@@ -50,7 +53,7 @@ public class MinecraftProtocolVersion {
* @return The version number.
*/
public static int getCurrentVersion() {
return getVersion(ProtocolLibrary.getProtocolManager().getMinecraftVersion());
return getVersion(MinecraftVersion.getCurrentVersion());
}
/**
@@ -41,6 +41,7 @@ import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
@@ -62,8 +63,8 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavaibleException;
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavaibleException.Reason;
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavailableException;
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavailableException.Reason;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.comphenix.protocol.wrappers.nbt.NbtType;
import com.google.common.base.Joiner;
@@ -128,13 +129,13 @@ public class MinecraftReflection {
private static String packageVersion;
// Item stacks
private static Method craftNMSMethod;
/* private static Method craftNMSMethod;
private static Method craftBukkitNMS;
private static Method craftBukkitOBC;
private static boolean craftItemStackFailed;
private static Constructor<?> craftNMSConstructor;
private static Constructor<?> craftBukkitConstructor;
private static Constructor<?> craftBukkitConstructor; */
// net.minecraft.server
private static Class<?> itemStackArrayClass;
@@ -152,6 +153,7 @@ public class MinecraftReflection {
// Whether or not we are using netty
private static Boolean cachedNetty;
private static Boolean cachedWatcherObject;
private MinecraftReflection() {
// No need to make this constructable.
@@ -408,14 +410,31 @@ public class MinecraftReflection {
return javaName.startsWith(MINECRAFT_PREFIX_PACKAGE) && javaName.endsWith(className);
}
/**
* Determine if a given Object is compatible with a given Class. That is,
* whether or not the Object is an instance of that Class or one of its
* subclasses. If either is null, false is returned.
*
* @param clazz Class to test for, may be null
* @param object the Object to test, may be null
* @return True if it is, false if not
* @see Class#isAssignableFrom(Class)
*/
public static boolean is(Class<?> clazz, Object object) {
if (clazz == null || object == null) {
return false;
}
return clazz.isAssignableFrom(object.getClass());
}
/**
* Determine if a given object is a ChunkPosition.
* @param obj - the object to test.
* @return TRUE if it can, FALSE otherwise.
*/
public static boolean isChunkPosition(Object obj) {
Class<?> chunkPosition = getChunkPositionClass();
return obj != null && chunkPosition != null && chunkPosition.isAssignableFrom(obj.getClass());
return is(getChunkPositionClass(), obj);
}
/**
@@ -424,8 +443,7 @@ public class MinecraftReflection {
* @return TRUE if it can, FALSE otherwise.
*/
public static boolean isBlockPosition(Object obj) {
Class<?> blockPosition = getBlockPositionClass();
return obj != null && blockPosition != null && blockPosition.isAssignableFrom(obj.getClass());
return is(getBlockPositionClass(), obj);
}
/**
@@ -434,7 +452,7 @@ public class MinecraftReflection {
* @return TRUE if it can, FALSE otherwise.
*/
public static boolean isChunkCoordIntPair(Object obj) {
return obj != null && getChunkCoordIntPair().isAssignableFrom(obj.getClass());
return is(getChunkCoordIntPair(), obj);
}
/**
@@ -443,8 +461,7 @@ public class MinecraftReflection {
* @return TRUE if it can, FALSE otherwise.
*/
public static boolean isChunkCoordinates(Object obj) {
Class<?> chunkCoordinates = getChunkCoordinatesClass();
return obj != null && chunkCoordinates != null && chunkCoordinates.isAssignableFrom(obj.getClass());
return is(getChunkCoordinatesClass(), obj);
}
/**
@@ -453,7 +470,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isPacketClass(Object obj) {
return obj != null && getPacketClass().isAssignableFrom(obj.getClass());
return is(getPacketClass(), obj);
}
/**
@@ -462,7 +479,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isLoginHandler(Object obj) {
return obj != null && getNetLoginHandlerClass().isAssignableFrom(obj.getClass());
return is(getNetLoginHandlerClass(), obj);
}
/**
@@ -471,7 +488,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isServerHandler(Object obj) {
return obj != null && getPlayerConnectionClass().isAssignableFrom(obj.getClass());
return is(getPlayerConnectionClass(), obj);
}
/**
@@ -480,7 +497,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isMinecraftEntity(Object obj) {
return obj != null && getEntityClass().isAssignableFrom(obj.getClass());
return is(getEntityClass(), obj);
}
/**
@@ -489,7 +506,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isItemStack(Object value) {
return value != null && getItemStackClass().isAssignableFrom(value.getClass());
return is(getItemStackClass(), value);
}
/**
@@ -498,7 +515,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isCraftPlayer(Object value) {
return value != null && getCraftPlayerClass().isAssignableFrom(value.getClass());
return is(getCraftPlayerClass(), value);
}
/**
@@ -507,7 +524,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isMinecraftPlayer(Object obj) {
return obj != null && getEntityPlayerClass().isAssignableFrom(obj.getClass());
return is(getEntityPlayerClass(), obj);
}
/**
@@ -516,7 +533,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isWatchableObject(Object obj) {
return obj != null && getWatchableObjectClass().isAssignableFrom(obj.getClass());
return is(getWatchableObjectClass(), obj);
}
/**
@@ -525,7 +542,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isDataWatcher(Object obj) {
return obj != null && getDataWatcherClass().isAssignableFrom(obj.getClass());
return is(getDataWatcherClass(), obj);
}
/**
@@ -534,7 +551,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isIntHashMap(Object obj) {
return obj != null && getIntHashMapClass().isAssignableFrom(obj.getClass());
return is(getIntHashMapClass(), obj);
}
/**
@@ -543,7 +560,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isCraftItemStack(Object obj) {
return obj != null && getCraftItemStackClass().isAssignableFrom(obj.getClass());
return is(getCraftItemStackClass(), obj);
}
/**
@@ -917,6 +934,9 @@ public class MinecraftReflection {
return getMinecraftClass("MinecraftServer");
} catch (RuntimeException e) {
useFallbackServer();
// Reset cache and try again
setMinecraftClass("MinecraftServer", null);
return getMinecraftClass("MinecraftServer");
}
}
@@ -965,8 +985,10 @@ public class MinecraftReflection {
try {
return getMinecraftClass("ServerConfigurationManager", "PlayerList");
} catch (RuntimeException e) {
// Try again
useFallbackServer();
// Reset cache and try again
setMinecraftClass("ServerConfigurationManager", null);
return getMinecraftClass("ServerConfigurationManager");
}
}
@@ -1180,24 +1202,7 @@ public class MinecraftReflection {
try {
return getMinecraftClass("ChunkPosition");
} catch (RuntimeException e) {
try {
Class<?> normalChunkGenerator = getCraftBukkitClass("generator.NormalChunkGenerator");
// ChunkPosition a(net.minecraft.server.World world, String string, int i, int i1, int i2) {
FuzzyMethodContract selected = FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.parameterMatches(getMinecraftObjectMatcher(), 0)
.parameterExactType(String.class, 1)
.parameterExactType(int.class, 2)
.parameterExactType(int.class, 3)
.parameterExactType(int.class, 4)
.build();
return setMinecraftClass("ChunkPosition",
FuzzyReflection.fromClass(normalChunkGenerator).getMethod(selected).getReturnType());
} catch (Throwable ex) {
return null;
}
return null;
}
}
@@ -1315,16 +1320,23 @@ public class MinecraftReflection {
}
public static Class<?> getDataWatcherObjectClass() {
// TODO Implement a fallback
return getMinecraftClass("DataWatcherObject");
try {
return getMinecraftClass("DataWatcherObject");
} catch (RuntimeException ex) {
return null;
}
}
public static boolean watcherObjectExists() {
try {
return getDataWatcherObjectClass() != null;
} catch (RuntimeException e) {
return false;
if (cachedWatcherObject == null) {
try {
return cachedWatcherObject = getDataWatcherObjectClass() != null;
} catch (Throwable ex) {
return cachedWatcherObject = false;
}
}
return cachedWatcherObject;
}
public static Class<?> getDataWatcherSerializerClass() {
@@ -1621,8 +1633,10 @@ public class MinecraftReflection {
try {
return getMinecraftClass("AttributeModifier");
} catch (RuntimeException e) {
// Initialize first
getAttributeSnapshotClass();
// Reset cache and try again
setMinecraftClass("AttributeModifier", null);
return getMinecraftClass("AttributeModifier");
}
}
@@ -1794,8 +1808,7 @@ public class MinecraftReflection {
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isPlayerInfoData(Object obj) {
Class<?> clazz = getPlayerInfoDataClass();
return clazz != null && obj.getClass().equals(clazz);
return is(getPlayerInfoDataClass(), obj);
}
/**
@@ -1822,119 +1835,123 @@ public class MinecraftReflection {
return getArrayClass(getMultiBlockChangeInfoClass());
}
/**
* Retrieve a CraftItemStack from a given ItemStack.
* @param bukkitItemStack - the Bukkit ItemStack to convert.
* @return A CraftItemStack as an ItemStack.
*/
public static ItemStack getBukkitItemStack(ItemStack bukkitItemStack) {
// Delegate this task to the method that can execute it
if (craftBukkitNMS != null)
return getBukkitItemByMethod(bukkitItemStack);
if (craftBukkitConstructor == null) {
try {
craftBukkitConstructor = getCraftItemStackClass().getConstructor(ItemStack.class);
} catch (Exception e) {
// See if this method works
if (!craftItemStackFailed)
return getBukkitItemByMethod(bukkitItemStack);
throw new RuntimeException("Cannot find CraftItemStack(org.bukkit.inventory.ItemStack).", e);
}
}
// Try to create the CraftItemStack
public static boolean signUpdateExists() {
try {
return (ItemStack) craftBukkitConstructor.newInstance(bukkitItemStack);
} catch (Exception e) {
throw new RuntimeException("Cannot construct CraftItemStack.", e);
return getMinecraftClass("PacketPlayOutUpdateSign") != null;
} catch (RuntimeException ex) {
return false;
}
}
private static ItemStack getBukkitItemByMethod(ItemStack bukkitItemStack) {
if (craftBukkitNMS == null) {
try {
craftBukkitNMS = getCraftItemStackClass().getMethod("asNMSCopy", ItemStack.class);
craftBukkitOBC = getCraftItemStackClass().getMethod("asCraftMirror", MinecraftReflection.getItemStackClass());
} catch (Exception e) {
craftItemStackFailed = true;
throw new RuntimeException("Cannot find CraftItemStack.asCraftCopy(org.bukkit.inventory.ItemStack).", e);
}
}
// Next, construct it
try {
Object nmsItemStack = craftBukkitNMS.invoke(null, bukkitItemStack);
return (ItemStack) craftBukkitOBC.invoke(null, nmsItemStack);
} catch (Exception e) {
throw new RuntimeException("Cannot construct CraftItemStack.", e);
}
public static Class<?> getNonNullListClass() {
return getMinecraftClass("NonNullList");
}
public static Class<?> getCraftSoundClass() {
return getCraftBukkitClass("CraftSound");
}
// ---- ItemStack conversions
private static Method asNMSCopy = null;
private static Method asCraftMirror = null;
private static Boolean nullEnforced = null;
private static Method isEmpty = null;
/**
* Retrieve the Bukkit ItemStack from a given net.minecraft.server ItemStack.
* @param minecraftItemStack - the NMS ItemStack to wrap.
* @return The wrapped ItemStack.
*/
public static ItemStack getBukkitItemStack(Object minecraftItemStack) {
// Delegate this task to the method that can execute it
if (craftNMSMethod != null)
return getBukkitItemByMethod(minecraftItemStack);
if (craftNMSConstructor == null) {
try {
craftNMSConstructor = getCraftItemStackClass().getConstructor(minecraftItemStack.getClass());
} catch (Exception e) {
// Give it a try
if (!craftItemStackFailed)
return getBukkitItemByMethod(minecraftItemStack);
throw new RuntimeException("Cannot find CraftItemStack(net.minecraft.server.ItemStack).", e);
}
}
// Try to create the CraftItemStack
try {
return (ItemStack) craftNMSConstructor.newInstance(minecraftItemStack);
} catch (Exception e) {
throw new RuntimeException("Cannot construct CraftItemStack.", e);
}
}
private static ItemStack getBukkitItemByMethod(Object minecraftItemStack) {
if (craftNMSMethod == null) {
try {
craftNMSMethod = getCraftItemStackClass().getMethod("asCraftMirror", minecraftItemStack.getClass());
} catch (Exception e) {
craftItemStackFailed = true;
throw new RuntimeException("Cannot find CraftItemStack.asCraftMirror(net.minecraft.server.ItemStack).", e);
}
}
// Next, construct it
try {
return (ItemStack) craftNMSMethod.invoke(null, minecraftItemStack);
} catch (Exception e) {
throw new RuntimeException("Cannot construct CraftItemStack.", e);
}
}
/**
* Retrieve the net.minecraft.server ItemStack from a Bukkit ItemStack.
* <p>
* By convention, item stacks that contain air are usually represented as NULL.
* Retrieves the Bukkit equivalent of a NMS ItemStack. This method should
* preserve NBT data and will never return null when supplied with a valid
* ItemStack. Empty ItemStacks are treated as AIR.
*
* @param stack - the Bukkit ItemStack to convert.
* @return The NMS ItemStack, or NULL if the stack represents air.
* @param generic NMS ItemStack
* @return The Bukkit equivalent
*/
public static Object getMinecraftItemStack(ItemStack stack) {
// Make sure this is a CraftItemStack
if (!isCraftItemStack(stack))
stack = getBukkitItemStack(stack);
public static ItemStack getBukkitItemStack(Object generic) {
if (generic == null) {
// Convert null to AIR - 1.11 behavior
return new ItemStack(Material.AIR);
}
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
return unwrapper.unwrapItem(stack);
if (generic instanceof ItemStack) {
ItemStack bukkit = (ItemStack) generic;
// They're probably looking for the CraftItemStack
// If it's one already our work is done
if (is(getCraftItemStackClass(), generic)) {
return bukkit;
}
// If not, convert it to one
Object nmsStack = getMinecraftItemStack((ItemStack) generic);
return getBukkitItemStack(nmsStack);
}
if (!is(getItemStackClass(), generic)) {
// We can't do anything with non-ItemStacks
throw new IllegalArgumentException(generic + " is not an ItemStack!");
}
try {
// Check null enforcement - 1.11 behavior
if (nullEnforced == null) {
isEmpty = getItemStackClass().getMethod("isEmpty");
nullEnforced = true;
}
if (nullEnforced) {
if ((boolean) isEmpty.invoke(generic)) {
return new ItemStack(Material.AIR);
}
}
} catch (ReflectiveOperationException ex) {
nullEnforced = false;
}
if (asCraftMirror == null) {
try {
asCraftMirror = getCraftItemStackClass().getMethod("asCraftMirror", getItemStackClass());
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to obtain CraftItemStack.asCraftMirror", ex);
}
}
try {
// Convert to a craft mirror to preserve NBT data
return (ItemStack) asCraftMirror.invoke(nullEnforced, generic);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to obtain craft mirror of " + generic, ex);
}
}
/**
* Retrieves the NMS equivalent of a Bukkit ItemStack. This method will
* never return null and should preserve NBT data. Null inputs are treated
* as empty (AIR) ItemStacks.
*
* @param specific Bukkit ItemStack
* @return The NMS equivalent
*/
public static Object getMinecraftItemStack(ItemStack specific) {
if (asNMSCopy == null) {
try {
asNMSCopy = getCraftItemStackClass().getMethod("asNMSCopy", ItemStack.class);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to obtain CraftItemStack.asNMSCopy", ex);
}
}
if (is(getCraftItemStackClass(), specific)) {
// If it's already a CraftItemStack, use its handle
return new BukkitUnwrapper().unwrapItem(specific);
}
try {
// If not, grab a NMS copy
return asNMSCopy.invoke(null, specific);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to make NMS copy of " + specific, ex);
}
}
/**
@@ -1999,7 +2016,7 @@ public class MinecraftReflection {
// Attempt to use MCPC
try {
return classSource = new RemappedClassSource().initialize();
} catch (RemapperUnavaibleException e) {
} catch (RemapperUnavailableException e) {
if (e.getReason() != Reason.MCPC_NOT_PRESENT)
reporter.reportWarning(MinecraftReflection.class, Report.newBuilder(REPORT_CANNOT_FIND_MCPC_REMAPPER));
} catch (Exception e) {
@@ -44,6 +44,21 @@ public class MinecraftVersion implements Comparable<MinecraftVersion>, Serializa
*/
private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-\\.]+)\\s*\\)");
/**
* Version 1.12 - the world of color update.
*/
public static final MinecraftVersion COLOR_UPDATE = new MinecraftVersion("1.12");
/**
* Version 1.11 - the exploration update.
*/
public static final MinecraftVersion EXPLORATION_UPDATE = new MinecraftVersion("1.11");
/**
* Version 1.10 - the frostburn update.
*/
public static final MinecraftVersion FROSTBURN_UPDATE = new MinecraftVersion("1.10");
/**
* Version 1.9 - the combat update.
*/
@@ -28,7 +28,7 @@ import org.bukkit.Server;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.MethodUtils;
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavaibleException.Reason;
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavailableException.Reason;
class RemappedClassSource extends ClassSource {
private Object classRemapper;
@@ -53,7 +53,7 @@ class RemappedClassSource extends ClassSource {
/**
* Attempt to load the MCPC remapper.
* @return TRUE if we succeeded, FALSE otherwise.
* @throws RemapperUnavaibleException If the remapper is not present.
* @throws RemapperUnavailableException If the remapper is not present.
*/
public RemappedClassSource initialize() {
try {
@@ -64,14 +64,14 @@ class RemappedClassSource extends ClassSource {
String version = server.getVersion();
if (!version.contains("MCPC") && !version.contains("Cauldron")) {
throw new RemapperUnavaibleException(Reason.MCPC_NOT_PRESENT);
throw new RemapperUnavailableException(Reason.MCPC_NOT_PRESENT);
}
// Obtain the Class remapper used by MCPC+/Cauldron
this.classRemapper = FieldUtils.readField(getClass().getClassLoader(), "remapper", true);
if (this.classRemapper == null) {
throw new RemapperUnavaibleException(Reason.REMAPPER_DISABLED);
throw new RemapperUnavailableException(Reason.REMAPPER_DISABLED);
}
// Initialize some fields and methods used by the Jar Remapper
@@ -82,7 +82,7 @@ class RemappedClassSource extends ClassSource {
return this;
} catch (RemapperUnavaibleException e) {
} catch (RemapperUnavailableException e) {
throw e;
} catch (Exception e) {
// Damn it
@@ -115,7 +115,7 @@ class RemappedClassSource extends ClassSource {
}
}
public static class RemapperUnavaibleException extends RuntimeException {
public static class RemapperUnavailableException extends RuntimeException {
private static final long serialVersionUID = 1L;
public enum Reason {
@@ -139,7 +139,7 @@ class RemappedClassSource extends ClassSource {
private final Reason reason;
public RemapperUnavaibleException(Reason reason) {
public RemapperUnavailableException(Reason reason) {
super(reason.getMessage());
this.reason = reason;
}
@@ -1,92 +0,0 @@
package com.comphenix.protocol.utility;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
/**
* Allows us to stay backwards compatible with older versions of Bukkit.
*
* @author Kristian
*/
public class WrappedScheduler {
/**
* Represents a backwards compatible Bukkit task.
*/
public static interface TaskWrapper {
/**
* Cancel the current task.
*/
public void cancel();
}
/**
* Schedule a given task for a single asynchronous execution.
* @param plugin - the owner plugin.
* @param runnable - the task to run.
* @param firstDelay - the amount of time to wait until executing the task.
* @return A cancel token.
*/
public static TaskWrapper runAsynchronouslyOnce(final Plugin plugin, Runnable runnable, long firstDelay) {
return runAsynchronouslyRepeat(plugin, plugin.getServer().getScheduler(), runnable, firstDelay, -1L);
}
/**
* Schedule a given task for multiple asynchronous executions.
* @param plugin - the owner plugin.
* @param runnable - the task to run.
* @param firstDelay - the amount of time to wait until executing the task for the first time.
* @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once.
* @return A cancel token.
*/
public static TaskWrapper runAsynchronouslyRepeat(final Plugin plugin, Runnable runnable, long firstDelay, long repeatDelay) {
return runAsynchronouslyRepeat(plugin, plugin.getServer().getScheduler(), runnable, firstDelay, repeatDelay);
}
/**
* Schedule a given task for asynchronous execution.
* @param plugin - the owner plugin.
* @param scheduler - the current Bukkit scheduler.
* @param runnable - the task to run.
* @param firstDelay - the amount of time to wait until executing the task for the first time.
* @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once.
* @return A cancel token.
*/
public static TaskWrapper runAsynchronouslyRepeat(final Plugin plugin, final BukkitScheduler scheduler, Runnable runnable, long firstDelay, long repeatDelay) {
try {
@SuppressWarnings("deprecation")
final int taskID = scheduler.scheduleAsyncRepeatingTask(plugin, runnable, firstDelay, repeatDelay);
// Return the cancellable object
return new TaskWrapper() {
@Override
public void cancel() {
scheduler.cancelTask(taskID);
}
};
} catch (NoSuchMethodError e) {
return tryUpdatedVersion(plugin, scheduler, runnable, firstDelay, repeatDelay);
}
}
/**
* Attempt to do the same with the updated scheduling method.
* @param plugin - the owner plugin.
* @param scheduler - the current Bukkit scheduler.
* @param runnable - the task to run.
* @param firstDelay - the amount of time to wait until executing the task for the first time.
* @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once.
* @return A cancel token.
*/
private static TaskWrapper tryUpdatedVersion(final Plugin plugin, final BukkitScheduler scheduler, Runnable runnable, long firstDelay, long repeatDelay) {
final BukkitTask task = scheduler.runTaskTimerAsynchronously(plugin, runnable, firstDelay, repeatDelay);
return new TaskWrapper() {
@Override
public void cancel() {
task.cancel();
}
};
}
}
@@ -47,4 +47,26 @@ public abstract class AbstractWrapper {
public Class<?> getHandleType() {
return handleType;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj instanceof AbstractWrapper) {
AbstractWrapper that = (AbstractWrapper) obj;
return this.handle.equals(that.handle);
}
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
@Override
public String toString() {
return getClass().getName() + "[handle=" + handle + "]";
}
}
@@ -14,7 +14,6 @@
* 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.ref.WeakReference;
@@ -22,23 +21,9 @@ import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Set;
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.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
@@ -65,6 +50,16 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
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.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
/**
* Contains several useful equivalent converters for normal Bukkit types.
*
@@ -122,9 +117,9 @@ public class BukkitConverters {
* Represents a typical equivalence converter.
*
* @author Kristian
* @param <T> - type that can be converted.
* @param <TType> - type that can be converted.
*/
private static abstract class IgnoreNullConverter<TType> implements EquivalentConverter<TType> {
public static abstract class IgnoreNullConverter<TType> implements EquivalentConverter<TType> {
@Override
public final Object getGeneric(Class<?> genericType, TType specific) {
if (specific != null)
@@ -158,20 +153,20 @@ public class BukkitConverters {
@Override
public boolean equals(Object obj) {
// Very short
if (this == obj)
return true;
if (obj == null)
return false;
// See if they're equivalent
if (this == obj) return true;
if (obj instanceof EquivalentConverter) {
@SuppressWarnings("rawtypes")
EquivalentConverter other = (EquivalentConverter) obj;
return Objects.equal(this.getSpecificType(), other.getSpecificType());
EquivalentConverter<?> that = (EquivalentConverter<?>) obj;
return Objects.equal(this.getSpecificType(), that.getSpecificType());
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(this.getSpecificType());
}
}
/**
@@ -194,23 +189,23 @@ public class BukkitConverters {
@Override
public boolean equals(Object obj) {
// More shortcuts
if (obj == this)
return true;
if (obj == null)
return false;
if (obj == this) return true;
// Add another constraint
if (obj instanceof WorldSpecificConverter && super.equals(obj)) {
@SuppressWarnings("rawtypes")
WorldSpecificConverter other = (WorldSpecificConverter) obj;
return Objects.equal(world, other.world);
WorldSpecificConverter<?> that = (WorldSpecificConverter<?>) obj;
return Objects.equal(this.world, that.world);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(this.getSpecificType(), this.world);
}
}
/**
* Retrieve an equivalent converter for a map of generic keys and primitive values.
* @param <T> Key type
@@ -713,24 +708,24 @@ public class BukkitConverters {
* @return Item stack converter.
*/
public static EquivalentConverter<ItemStack> getItemStackConverter() {
return new IgnoreNullConverter<ItemStack>() {
return new EquivalentConverter<ItemStack>() {
@Override
protected Object getGenericValue(Class<?> genericType, ItemStack specific) {
return MinecraftReflection.getMinecraftItemStack(specific);
}
@Override
protected ItemStack getSpecificValue(Object generic) {
public ItemStack getSpecific(Object generic) {
return MinecraftReflection.getBukkitItemStack(generic);
}
@Override
public Object getGeneric(Class<?> genericType, ItemStack specific) {
return MinecraftReflection.getMinecraftItemStack(specific);
}
@Override
public Class<ItemStack> getSpecificType() {
return ItemStack.class;
}
};
}
/**
* Retrieve the converter for the ServerPing packet in {@link PacketType.Status.Server#OUT_SERVER_INFO}.
* @return Server ping converter.
@@ -964,10 +959,21 @@ public class BukkitConverters {
};
}
private static MethodAccessor soundGetter = null;
private static MethodAccessor getSound = null;
private static MethodAccessor getSoundEffect = null;
private static FieldAccessor soundKey = null;
private static Map<String, Sound> soundIndex = null;
public static EquivalentConverter<Sound> getSoundConverter() {
if (getSound == null || getSoundEffect == null) {
Class<?> craftSound = MinecraftReflection.getCraftSoundClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(craftSound, true);
getSound = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getSound", String.class, new Class<?>[] { Sound.class }));
getSoundEffect = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getSoundEffect",
MinecraftReflection.getSoundEffectClass(), new Class<?>[] { String.class }));
}
return new IgnoreNullConverter<Sound>() {
@Override
@@ -977,26 +983,42 @@ public class BukkitConverters {
@Override
protected Object getGenericValue(Class<?> genericType, Sound specific) {
if (soundGetter == null) {
Class<?> soundEffects = MinecraftReflection.getMinecraftClass("SoundEffects");
FuzzyReflection fuzzy = FuzzyReflection.fromClass(soundEffects, true);
soundGetter = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getSound", MinecraftReflection.getSoundEffectClass(), new Class<?>[] { String.class }));
}
MinecraftKey key = MinecraftKey.fromEnum(specific);
return soundGetter.invoke(null, key.getFullKey());
// Getting the SoundEffect is easy, Bukkit provides us the methods
String key = (String) getSound.invoke(null, specific);
return getSoundEffect.invoke(null, key);
}
@Override
protected Sound getSpecificValue(Object generic) {
// Getting the Sound is a bit more complicated...
if (soundKey == null) {
Class<?> soundEffect = generic.getClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(soundEffect, true);
soundKey = Accessors.getFieldAccessor(fuzzy.getFieldByType("key", MinecraftReflection.getMinecraftKeyClass()));
}
MinecraftKey key = MinecraftKey.fromHandle(soundKey.get(generic));
return Sound.valueOf(key.getEnumFormat());
MinecraftKey minecraftKey = MinecraftKey.fromHandle(soundKey.get(generic));
String key = minecraftKey.getKey();
// Use our index if it already exists
if (soundIndex != null) {
return soundIndex.get(key);
}
// If it doesn't, try to guess the enum name
try {
return Sound.valueOf(minecraftKey.getEnumFormat());
} catch (IllegalArgumentException ignored) {
}
// Worst case we index all the sounds and use it later
soundIndex = new ConcurrentHashMap<>();
for (Sound sound : Sound.values()) {
String index = (String) getSound.invoke(null, sound);
soundIndex.put(index, sound);
}
return soundIndex.get(key);
}
};
}
@@ -25,7 +25,6 @@ import net.md_5.bungee.chat.ComponentSerializer;
* Note: The BungeeCord Chat API is not included in CraftBukkit.
* @author dmulloy2
*/
public final class ComponentConverter {
private ComponentConverter() {
@@ -0,0 +1,60 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 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.wrappers;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Method;
/**
* Handles component parsing in 1.8
* @author dmulloy2
*/
public class ComponentParser {
private ComponentParser() {
}
public static Object deserialize(Object gson, Class<?> component, StringReader str) {
try {
com.google.gson.stream.JsonReader reader = new com.google.gson.stream.JsonReader(str);
reader.setLenient(true);
return ((com.google.gson.Gson) gson).getAdapter(component).read(reader);
} catch (IOException ex) {
throw new RuntimeException("Failed to read JSON", ex);
} catch (LinkageError er) {
return deserializeLegacy(gson, component, str);
}
}
// 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);
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);
}
}
}
@@ -1,6 +1,7 @@
package com.comphenix.protocol.wrappers;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.bukkit.GameMode;
@@ -216,7 +217,10 @@ public abstract class EnumWrappers {
DRAGON_BREATH("dragonbreath", 42, false),
END_ROD("endRod", 43, false),
DAMAGE_INDICATOR("damageIndicator", 44, true),
SWEEP_ATTACK("sweepAttack", 45, true);
SWEEP_ATTACK("sweepAttack", 45, true),
FALLING_DUST("fallingdust", 46, false, 1),
TOTEM("totem", 47, false),
SPIT("spit", 48, true);
private static final Map<String, Particle> BY_NAME;
private static final Map<Integer, Particle> BY_ID;
@@ -226,7 +230,7 @@ public abstract class EnumWrappers {
BY_NAME = new HashMap<String, Particle>();
for (Particle particle : values()) {
BY_NAME.put(particle.getName().toLowerCase(), particle);
BY_NAME.put(particle.getName().toLowerCase(Locale.ENGLISH), particle);
BY_ID.put(particle.getId(), particle);
}
}
@@ -264,7 +268,7 @@ public abstract class EnumWrappers {
}
public static Particle getByName(String name) {
return BY_NAME.get(name.toLowerCase());
return BY_NAME.get(name.toLowerCase(Locale.ENGLISH));
}
public static Particle getById(int id) {
@@ -302,7 +306,7 @@ public abstract class EnumWrappers {
}
public static SoundCategory getByKey(String key) {
return LOOKUP.get(key.toLowerCase());
return LOOKUP.get(key.toLowerCase(Locale.ENGLISH));
}
}
@@ -320,6 +324,31 @@ public abstract class EnumWrappers {
OFF_HAND;
}
public enum Direction {
DOWN,
UP,
NORTH,
SOUTH,
WEST,
EAST;
}
public enum ChatType {
CHAT(0),
SYSTEM(1),
GAME_INFO(2);
private byte id;
ChatType(int id) {
this.id = (byte) id;
}
public byte getId() {
return id;
}
}
private static Class<?> PROTOCOL_CLASS = null;
private static Class<?> CLIENT_COMMAND_CLASS = null;
private static Class<?> CHAT_VISIBILITY_CLASS = null;
@@ -338,6 +367,8 @@ public abstract class EnumWrappers {
private static Class<?> SOUND_CATEGORY_CLASS = null;
private static Class<?> ITEM_SLOT_CLASS = null;
private static Class<?> HAND_CLASS = null;
private static Class<?> DIRECTION_CLASS = null;
private static Class<?> CHAT_TYPE_CLASS = null;
private static boolean INITIALIZED = false;
private static Map<Class<?>, EquivalentConverter<?>> FROM_NATIVE = Maps.newHashMap();
@@ -373,6 +404,8 @@ public abstract class EnumWrappers {
SOUND_CATEGORY_CLASS = getEnum(PacketType.Play.Server.CUSTOM_SOUND_EFFECT.getPacketClass(), 0);
ITEM_SLOT_CLASS = getEnum(PacketType.Play.Server.ENTITY_EQUIPMENT.getPacketClass(), 0);
HAND_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 1);
DIRECTION_CLASS = getEnum(PacketType.Play.Client.USE_ITEM.getPacketClass(), 0);
CHAT_TYPE_CLASS = getEnum(PacketType.Play.Server.CHAT.getPacketClass(), 0);
associate(PROTOCOL_CLASS, Protocol.class, getClientCommandConverter());
associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
@@ -392,6 +425,8 @@ public abstract class EnumWrappers {
associate(SOUND_CATEGORY_CLASS, SoundCategory.class, getSoundCategoryConverter());
associate(ITEM_SLOT_CLASS, ItemSlot.class, getItemSlotConverter());
associate(HAND_CLASS, Hand.class, getHandConverter());
associate(DIRECTION_CLASS, Direction.class, getDirectionConverter());
associate(CHAT_TYPE_CLASS, ChatType.class, getChatTypeConverter());
INITIALIZED = true;
}
@@ -515,6 +550,16 @@ public abstract class EnumWrappers {
return HAND_CLASS;
}
public static Class<?> getDirectionClass() {
initialize();
return DIRECTION_CLASS;
}
public static Class<?> getChatTypeClass() {
initialize();
return CHAT_TYPE_CLASS;
}
// Get the converters
public static EquivalentConverter<Protocol> getProtocolConverter() {
return new EnumConverter<Protocol>(Protocol.class);
@@ -588,6 +633,14 @@ public abstract class EnumWrappers {
return new EnumConverter<Hand>(Hand.class);
}
public static EquivalentConverter<Direction> getDirectionConverter() {
return new EnumConverter<Direction>(Direction.class);
}
public static EquivalentConverter<ChatType> getChatTypeConverter() {
return new EnumConverter<ChatType>(ChatType.class);
}
/**
* Retrieve a generic enum converter for use with StructureModifiers.
* @param enumClass - Enum class
@@ -17,6 +17,7 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import java.util.Locale;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
@@ -68,9 +69,11 @@ public class MinecraftKey {
* is lower case, with underscores replaced by periods.
* @param value The value
* @return The resulting key
* @deprecated This isn't accurate in all cases
*/
@Deprecated
public static MinecraftKey fromEnum(Enum<?> value) {
return new MinecraftKey(value.name().toLowerCase().replace("_", "."));
return new MinecraftKey(value.name().toLowerCase(Locale.ENGLISH).replace("_", "."));
}
/**
@@ -102,9 +105,11 @@ public class MinecraftKey {
* Returns this key back into Enum format, upper case with periods replaced
* by underscores.
* @return The enum format
* @deprecated This isn't accurate in all cases
*/
@Deprecated
public String getEnumFormat() {
return key.toUpperCase().replace(".", "_");
return key.toUpperCase(Locale.ENGLISH).replace(".", "_");
}
private static Constructor<?> constructor = null;
@@ -0,0 +1,129 @@
/**
* (c) 2016 dmulloy2
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BukkitConverters.IgnoreNullConverter;
/**
* @author dmulloy2
*/
public class Vector3F {
protected float x;
protected float y;
protected float z;
public Vector3F() {
this(0, 0, 0);
}
public Vector3F(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public float getX() {
return x;
}
public Vector3F setX(float x) {
this.x = x;
return this;
}
public float getY() {
return y;
}
public Vector3F setY(float y) {
this.y = y;
return this;
}
public float getZ() {
return z;
}
public Vector3F setZ(float z) {
this.z = z;
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Float.floatToIntBits(x);
result = prime * result + Float.floatToIntBits(y);
result = prime * result + Float.floatToIntBits(z);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof Vector3F) {
Vector3F that = (Vector3F) obj;
if (Float.floatToIntBits(x) != Float.floatToIntBits(that.x))
return false;
if (Float.floatToIntBits(y) != Float.floatToIntBits(that.y))
return false;
if (Float.floatToIntBits(z) != Float.floatToIntBits(that.z))
return false;
return true;
}
return false;
}
private static Constructor<?> constructor = null;
private static Class<?> clazz = MinecraftReflection.getMinecraftClass("Vector3f");
public static Class<?> getMinecraftClass() {
return clazz;
}
public static EquivalentConverter<Vector3F> getConverter() {
return new IgnoreNullConverter<Vector3F>() {
@Override
public Class<Vector3F> getSpecificType() {
return Vector3F.class;
}
@Override
protected Object getGenericValue(Class<?> genericType, Vector3F specific) {
if (constructor == null) {
try {
constructor = clazz.getConstructor(float.class, float.class, float.class);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to find constructor for Vector3f", ex);
}
}
try {
return constructor.newInstance(specific.x, specific.y, specific.z);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to create new instance of Vector3f", ex);
}
}
@Override
protected Vector3F getSpecificValue(Object generic) {
StructureModifier<Float> modifier = new StructureModifier<Float>(generic.getClass())
.withTarget(generic).withType(float.class);
float x = modifier.read(0);
float y = modifier.read(1);
float z = modifier.read(2);
return new Vector3F(x, y, z);
}
};
}
}
@@ -195,16 +195,16 @@ public class WrappedAttribute extends AbstractWrapper {
if (obj instanceof WrappedAttribute) {
WrappedAttribute other = (WrappedAttribute) obj;
return getBaseValue() == other.getBaseValue() &&
Objects.equal(getAttributeKey(), other.getAttributeKey()) &&
Sets.symmetricDifference(
getModifiers(),
other.getModifiers()
).isEmpty();
if (getBaseValue() == other.getBaseValue() &&
Objects.equal(getAttributeKey(), other.getAttributeKey())) {
return getModifiers().stream()
.filter((elem) -> !other.getModifiers().contains(elem))
.count() == 0;
}
}
return false;
}
@Override
public int hashCode() {
if (attributeModifiers == null)
@@ -251,12 +251,7 @@ public class WrappedAttribute extends AbstractWrapper {
@Override
public String toString() {
return Objects.toStringHelper("WrappedAttribute").
add("key", getAttributeKey()).
add("baseValue", getBaseValue()).
add("finalValue", getFinalValue()).
add("modifiers", getModifiers()).
toString();
return "WrappedAttribute[key=" + getAttributeKey() + ", base=" + getBaseValue() + ", final=" + getFinalValue() + ", modifiers=" + getModifiers() + "]";
}
/**
@@ -149,18 +149,35 @@ public class WrappedBlockData extends AbstractWrapper {
return new WrappedBlockData(blockData);
}
/**
* Retrieve a deep copy of the current wrapper object.
* @return The cloned object.
*/
public WrappedBlockData deepClone() {
return WrappedBlockData.createData(getType(), getData());
}
@Override
public String toString() {
return "WrappedBlockData[handle=" + handle + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getType().hashCode();
result = prime * result + getData();
return result;
}
@Override
public boolean equals(Object o) {
if (o instanceof WrappedBlockData) {
WrappedBlockData that = (WrappedBlockData) o;
return this.getType() == that.getType();
return this.getType() == that.getType() && getData() == that.getData();
}
return false;
}
}
}
@@ -1,10 +1,10 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Method;
import java.util.List;
import java.io.StringReader;
import org.bukkit.ChatColor;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
@@ -19,18 +19,35 @@ import com.google.common.base.Preconditions;
public class WrappedChatComponent extends AbstractWrapper {
private static final Class<?> SERIALIZER = MinecraftReflection.getChatSerializerClass();
private static final Class<?> COMPONENT = MinecraftReflection.getIChatBaseComponentClass();
private static final Class<?> GSON_CLASS = MinecraftReflection.getMinecraftGsonClass();
private static Object GSON = null;
private static MethodAccessor DESERIALIZE = null;
private static MethodAccessor SERIALIZE_COMPONENT = null;
private static MethodAccessor DESERIALIZE_COMPONENT = null;
private static MethodAccessor CONSTRUCT_COMPONENT = null;
private static ConstructorAccessor CONSTRUCT_TEXT_COMPONENT = null;
static {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true);
// Retrieve the correct methods
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("serialize", /* a */
String.class, new Class<?>[] { COMPONENT }));
DESERIALIZE_COMPONENT = findDeserialize(fuzzy);
try {
GSON = FieldUtils.readStaticField(fuzzy.getFieldByType("gson", GSON_CLASS), true);
} catch (IllegalAccessException ex) {
throw new RuntimeException("Failed to obtain GSON field", ex);
}
try {
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getMinecraftClass("ChatDeserializer"), true)
.getMethodByParameters("deserialize", Object.class, new Class<?>[] { GSON_CLASS, String.class, Class.class, boolean.class }));
} catch (IllegalArgumentException ex) {
// We'll handle it in the ComponentParser
DESERIALIZE = null;
}
// Get a component from a standard Minecraft message
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(MinecraftReflection.getCraftChatMessage(), "fromString", String.class);
@@ -39,23 +56,17 @@ public class WrappedChatComponent extends AbstractWrapper {
CONSTRUCT_TEXT_COMPONENT = Accessors.getConstructorAccessor(MinecraftReflection.getChatComponentTextClass(), String.class);
}
private static MethodAccessor findDeserialize(FuzzyReflection fuzzy) {
List<Method> methods = fuzzy.getMethodListByParameters(COMPONENT, new Class<?>[] { String.class });
if (methods.isEmpty()) {
throw new IllegalArgumentException("Unable to find deserialize method in " + fuzzy.getSource().getName());
private static Object deserialize(String json) {
// Should be non-null on 1.9 and up
if (DESERIALIZE != null) {
return DESERIALIZE.invoke(null, GSON, json, COMPONENT, true);
}
// Try to find b, we want leniency
for (Method method : methods) {
if (method.getName().equals("b")) {
return Accessors.getMethodAccessor(method);
}
}
// Oh well
return Accessors.getMethodAccessor(methods.get(0));
// Mock leniency behavior in 1.8
StringReader str = new StringReader(json);
return ComponentParser.deserialize(GSON, COMPONENT, str);
}
private transient String cache;
private WrappedChatComponent(Object handle, String cache) {
@@ -79,7 +90,7 @@ public class WrappedChatComponent extends AbstractWrapper {
* @return The chat component wrapper.
*/
public static WrappedChatComponent fromJson(String json) {
return new WrappedChatComponent(DESERIALIZE_COMPONENT.invoke(null, json), json);
return new WrappedChatComponent(deserialize(json), json);
}
/**
@@ -127,7 +138,7 @@ public class WrappedChatComponent extends AbstractWrapper {
* @param obj - the JSON that represents the new component.
*/
public void setJson(String obj) {
this.handle = DESERIALIZE_COMPONENT.invoke(null, obj);
this.handle = deserialize(obj);
this.cache = obj;
}
@@ -138,21 +149,6 @@ public class WrappedChatComponent extends AbstractWrapper {
public WrappedChatComponent deepClone() {
return fromJson(getJson());
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof WrappedChatComponent) {
return ((WrappedChatComponent) obj).handle.equals(handle);
}
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
@Override
public String toString() {
@@ -19,7 +19,6 @@ package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
/**
* Allows access to a chunk coordinate.
@@ -161,24 +160,6 @@ public class WrappedChunkCoordinate extends AbstractWrapper implements Comparabl
return ((Comparable<Object>) handle).compareTo(other.handle);
}
@Override
public boolean equals(Object other) {
if (other instanceof WrappedChunkCoordinate) {
WrappedChunkCoordinate wrapper = (WrappedChunkCoordinate) other;
return Objects.equal(handle, wrapper.handle);
}
// It's tempting to handle the ChunkCoordinate case too, but then
// the equals() method won't be commutative, causing a.equals(b) to
// be different to b.equals(a).
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
@Override
public String toString() {
return String.format("ChunkCoordinate [x: %s, y: %s, z: %s]", getX(), getY(), getZ());
@@ -16,19 +16,8 @@
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import java.lang.reflect.*;
import java.util.*;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.FieldAccessException;
@@ -43,9 +32,14 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableBiMap;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
/**
* Represents a DataWatcher in 1.9
* Represents a DataWatcher in 1.8 thru 1.10
*
* @author dmulloy2
*/
@@ -108,8 +102,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
public WrappedDataWatcher(List<WrappedWatchableObject> objects) {
this();
for (WrappedWatchableObject object : objects) {
setObject(object.getWatcherObject(), object);
if (MinecraftReflection.watcherObjectExists()) {
for (WrappedWatchableObject object : objects) {
setObject(object.getWatcherObject(), object);
}
} else {
for (WrappedWatchableObject object : objects) {
setObject(object.getIndex(), object);
}
}
}
@@ -216,6 +216,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
/**
* @deprecated Renamed to {@link #remove(int)}
*/
@Deprecated
public WrappedWatchableObject removeObject(int index) {
return remove(index);
}
/**
* Removes the item at a given index.
*
@@ -234,7 +242,15 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return True if it does, false if not
*/
public boolean hasIndex(int index) {
return getMap().containsKey(index);
return getObject(index) != null;
}
/**
* Returns a set containing all the registered indexes
* @return The set
*/
public Set<Integer> indexSet() {
return getMap().keySet();
}
/**
@@ -321,59 +337,114 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* Retrieve a watchable object by index.
*
* @param index Index of the object to retrieve.
* @return The watched object.
* @return The watched object or null if it doesn't exist.
*/
public Object getObject(int index) {
return getObject(new WrappedDataWatcherObject(index, null));
return getObject(WrappedDataWatcherObject.fromIndex(index));
}
/**
* Retrieve a watchable object by watcher object.
*
* @param object The watcher object
* @return The watched object
* @return The watched object or null if it doesn't exist.
*/
public Object getObject(WrappedDataWatcherObject object) {
Validate.notNull(object, "Watcher object cannot be null!");
if (GETTER == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType);
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
if (MinecraftReflection.watcherObjectExists()) {
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactType(object.getHandleType())
.returnTypeExact(Object.class)
.build(), "get"));
} else {
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactType(int.class)
.returnTypeExact(MinecraftReflection.getDataWatcherItemClass())
.build()));
}
}
Object value = GETTER.invoke(handle, object.getHandle());
return WrappedWatchableObject.getWrapped(value);
try {
Object value = GETTER.invoke(handle, object.getHandle());
return WrappedWatchableObject.getWrapped(value);
} catch (RuntimeException ex) {
// Nothing exists at this index
return null;
}
}
// ---- Object Setters
/**
* Sets the DataWatcher Item at a given index to a new value.
* Sets the DataWatcher Item at a given index to a new value. In 1.9 and up,
* you cannot register objects without a watcher object.
*
* @param index Index of the object to set
* @param value New value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(WrappedDataWatcherObject, Object)}
* @see {@link #setObject(WrappedDataWatcherObject, Object, boolean)}
* @throws IllegalArgumentException in 1.9 and up if there isn't already an
* object at this index
*/
public void setObject(int index, Object value, boolean update) {
if (MinecraftReflection.watcherObjectExists() && !hasIndex(index)) {
throw new IllegalArgumentException("You cannot register objects without a watcher object!");
}
setObject(WrappedDataWatcherObject.fromIndex(index), value, update);
}
/**
* Shortcut for {@link #setObject(int, Object, boolean)}
*/
public void setObject(int index, Object value) {
Validate.isTrue(hasIndex(index), "You cannot register objects without the watcher object!");
setObject(index, null, value);
setObject(index, value, false);
}
/**
* Sets the DataWatcher Item at a given index to a new value.
*
* @param index Index of the object to set
* @param Serializer Serializer from {@link Serializer#get(Class)}
* @param serializer Serializer from {@link Registry#get(Class)}
* @param value New value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(WrappedDataWatcherObject, Object)}
*/
public void setObject(int index, Serializer serializer, Object value, boolean update) {
setObject(new WrappedDataWatcherObject(index, serializer), value, update);
}
/**
* Alias for {@link #setObject(int, Serializer, Object, boolean)}
*/
public void setObject(int index, Serializer serializer, Object value) {
setObject(new WrappedDataWatcherObject(index, serializer), value);
setObject(new WrappedDataWatcherObject(index, serializer), value, false);
}
/**
* Sets the DataWatcher Item at a given index to a new value.
*
* @param index Index of the object to set
* @param value New value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(int, Object, boolean)}
*/
public void setObject(int index, WrappedWatchableObject value, boolean update) {
setObject(index, value.getRawValue(), update);
}
/**
* Alias for {@link #setObject(int, WrappedWatchableObject, boolean)}
*/
public void setObject(int index, WrappedWatchableObject value) {
setObject(index, value.getRawValue(), false);
}
/**
@@ -381,11 +452,19 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
*
* @param object Associated watcher object
* @param value Wrapped value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(WrappedDataWatcherObject, Object)}
* @see #setObject(WrappedDataWatcherObject, Object)
*/
public void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value, boolean update) {
setObject(object, value.getRawValue(), update);
}
/**
* Shortcut for {@link #setObject(WrappedDataWatcherObject, WrappedWatchableObject, boolean)}
*/
public void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value) {
setObject(object, value.getRawValue());
setObject(object, value.getRawValue(), false);
}
/**
@@ -399,27 +478,46 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @throws IllegalArgumentException If the watcher object is null or must
* have a serializer and does not have one.
*/
public void setObject(WrappedDataWatcherObject object, Object value) {
public void setObject(WrappedDataWatcherObject object, Object value, boolean update) {
Validate.notNull(object, "Watcher object cannot be null!");
if (SETTER == null || REGISTER == null) {
if (SETTER == null && REGISTER == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
FuzzyMethodContract contract = FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.requireModifier(Modifier.PUBLIC)
.parameterExactArray(object.getHandleType(), Object.class)
.build();
SETTER = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "set"));
REGISTER = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "register"));
List<Method> methods = fuzzy.getMethodList(contract);
for (Method method : methods) {
if (method.getName().equals("set") || method.getName().equals("watch")) {
SETTER = Accessors.getMethodAccessor(method);
} else {
REGISTER = Accessors.getMethodAccessor(method);
}
}
}
// Unwrap the object
value = WrappedWatchableObject.getUnwrapped(value);
if (hasIndex(object.getIndex())) {
SETTER.invoke(handle, object.getHandle(), WrappedWatchableObject.getUnwrapped(value));
SETTER.invoke(handle, object.getHandle(), value);
} else {
Serializer serializer = object.getSerializer();
Validate.notNull(serializer, "You must specify a serializer to register an object!");
REGISTER.invoke(handle, object.getHandle(), WrappedWatchableObject.getUnwrapped(value));
object.checkSerializer();
REGISTER.invoke(handle, object.getHandle(), value);
}
if (update) {
getWatchableObject(object.getIndex()).setDirtyState(update);
}
}
/**
* Shortcut for {@link #setObject(WrappedDataWatcherObject, Object, boolean)}
*/
public void setObject(WrappedDataWatcherObject object, Object value) {
setObject(object, value, false);
}
// ---- Utility Methods
@@ -432,8 +530,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
public WrappedDataWatcher deepClone() {
WrappedDataWatcher clone = new WrappedDataWatcher(getEntity());
for (WrappedWatchableObject wrapper : this) {
clone.setObject(wrapper.getWatcherObject(), wrapper);
if (MinecraftReflection.watcherObjectExists()) {
for (WrappedWatchableObject wrapper : this) {
clone.setObject(wrapper.getWatcherObject(), wrapper);
}
} else {
for (WrappedWatchableObject wrapper : this) {
clone.setObject(wrapper.getIndex(), wrapper);
}
}
return clone;
@@ -479,26 +583,37 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
ENTITY_FIELD.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity));
}
private static final ImmutableBiMap<Class<?>, Integer> CLASS_TO_ID = new ImmutableBiMap.Builder<Class<?>, Integer>()
.put(Byte.class, 0)
.put(Short.class, 1)
.put(Integer.class, 2)
.put(Float.class, 3)
.put(String.class, 4)
.put(MinecraftReflection.getItemStackClass(), 5)
.put(MinecraftReflection.getBlockPositionClass(), 6)
.put(Vector3F.getMinecraftClass(), 7)
.build();
/**
* No longer supported in 1.9 due to the removal of a consistent type <-> ID map.
* Retrieves the type ID associated with a given class. No longer supported
* in 1.9 and up due to the removal of type IDs.
*
* @param clazz
* @return Null
* @param clazz Class to find ID for
* @return The ID, or null if not found
*/
@Deprecated
public static Integer getTypeID(Class<?> clazz) {
return null;
return CLASS_TO_ID.get(clazz);
}
/**
* No longer supported in 1.9 due to the removal of a consistent type <-> ID map.
* Retrieves the class associated with a given type ID. No longer
* supported in 1.9 and up due to the removal of type IDs.
*
* @param typeID
* @return Null
* @param typeID ID to find Class for
* @return The Class, or null if not found
*/
@Deprecated
public static Class<?> getTypeClass(int typeID) {
return null;
return CLASS_TO_ID.inverse().get(typeID);
}
@Override
@@ -544,12 +659,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
*
* @author dmulloy2
*/
public static class WrappedDataWatcherObject extends AbstractWrapper {
public static class WrappedDataWatcherObject {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherObjectClass();
private static ConstructorAccessor constructor = null;
private static MethodAccessor getSerializer = null;
private final StructureModifier<Object> modifier;
private StructureModifier<Object> modifier;
private Object handle;
protected WrappedDataWatcherObject() {
}
/**
* Creates a new watcher object from a NMS handle.
@@ -557,9 +676,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @param handle The handle
*/
public WrappedDataWatcherObject(Object handle) {
super(HANDLE_TYPE);
setHandle(handle);
this.handle = handle;
this.modifier = new StructureModifier<Object>(HANDLE_TYPE).withTarget(handle);
}
@@ -573,6 +690,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
this(newHandle(index, serializer));
}
static WrappedDataWatcherObject fromIndex(int index) {
if (MinecraftReflection.watcherObjectExists()) {
return new WrappedDataWatcherObject(index, null);
} else {
return new DummyWatcherObject(index);
}
}
private static Object newHandle(int index, Serializer serializer) {
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
@@ -616,6 +741,18 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
public void checkSerializer() {
Validate.notNull(getSerializer(), "You must specify a serializer to register an object!");
}
public Object getHandle() {
return handle;
}
public Class<?> getHandleType() {
return HANDLE_TYPE;
}
@Override
public String toString() {
return "DataWatcherObject[index=" + getIndex() + ", serializer=" + getSerializer() + "]";
@@ -624,7 +761,6 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof WrappedDataWatcherObject) {
WrappedDataWatcherObject other = (WrappedDataWatcherObject) obj;
@@ -633,6 +769,57 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
}
private static class DummyWatcherObject extends WrappedDataWatcherObject {
private final int index;
public DummyWatcherObject(int index) {
this.index = index;
}
@Override
public int getIndex() {
return index;
}
@Override
public Serializer getSerializer() {
return null;
}
@Override
public Object getHandle() {
return getIndex();
}
@Override
public Class<?> getHandleType() {
return int.class;
}
@Override
public void checkSerializer() {
// Do nothing
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof DummyWatcherObject) {
DummyWatcherObject that = (DummyWatcherObject) obj;
return this.index == that.index;
}
return false;
}
}
/**
@@ -697,7 +884,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* <li>Float</li>
* <li>String</li>
* <li>IChatBaseComponent</li>
* <li>Optional&lt;ItemStack&gt;</li>
* <li>ItemStack</li>
* <li>Optional&lt;IBlockData&gt;</li>
* <li>Boolean</li>
* <li>Vector3f</li>
@@ -705,6 +892,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* <li>Optional&lt;BlockPosition&gt;</li>
* <li>EnumDirection</li>
* <li>Optional&lt;UUID&gt;</li>
* <li>NBTTagCompound</li>
* </ul>
*
* @author dmulloy2
@@ -768,7 +956,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 cannot be null!");
initialize();
for (Serializer serializer : REGISTRY) {
@@ -820,5 +1008,75 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
}
// ---- Helper methods
/**
* Gets the serializer for IChatBaseComponents
* @return The serializer
*/
public static Serializer getChatComponentSerializer() {
return get(MinecraftReflection.getIChatBaseComponentClass());
}
/**
* Gets the serializer for ItemStacks
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getItemStackSerializer(boolean optional) {
return get(MinecraftReflection.getItemStackClass(), optional);
}
/**
* Gets the serializer for BlockData
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getBlockDataSerializer(boolean optional) {
return get(MinecraftReflection.getIBlockDataClass(), optional);
}
/**
* Gets the serializer for Vector3Fs
* @return The serializer
*/
public static Serializer getVectorSerializer() {
return get(Vector3F.getMinecraftClass());
}
/**
* Gets the serializer for BlockPositions
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getBlockPositionSerializer(boolean optional) {
return get(MinecraftReflection.getBlockPositionClass(), optional);
}
/**
* Gets the serializer for Directions
* @return The serializer
*/
public static Serializer getDirectionSerializer() {
return get(EnumWrappers.getDirectionClass());
}
/**
* Gets the serializer for UUIDs
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getUUIDSerializer(boolean optional) {
return get(UUID.class, optional);
}
/**
* Gets the serializer for NBT Compound tags
* @return The serializer
*/
public static Serializer getNBTCompoundSerializer() {
return get(MinecraftReflection.getNBTCompoundClass(), false);
}
}
}
@@ -1,9 +1,5 @@
package com.comphenix.protocol.wrappers;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
@@ -19,14 +15,13 @@ import org.bukkit.Server;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.Util;
@@ -34,25 +29,18 @@ import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
/**
* Represents a server ping packet data.
* @author Kristian
*/
public class WrappedServerPing extends AbstractWrapper {
/**
* Lookup of Minecraft versions and ping version numbers.
*/
private static ImmutableMap<MinecraftVersion, Integer> VERSION_NUMBERS = ImmutableMap.<MinecraftVersion, Integer>builder()
.put(MinecraftVersion.WORLD_UPDATE, 4)
.put(MinecraftVersion.SKIN_UPDATE, 5)
.put(MinecraftVersion.BOUNTIFUL_UPDATE, 47)
.build();
private static MinecraftVersion LAST_VERSION = MinecraftVersion.BOUNTIFUL_UPDATE;
private static Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
private static Class<?> GAME_PROFILE_ARRAY = MinecraftReflection.getArrayClass(GAME_PROFILE);
@@ -101,7 +89,7 @@ public class WrappedServerPing extends AbstractWrapper {
/**
* Construct a new server ping initialized with a zero player count, and zero maximum.
* <p>
* Note that the version string is set to 1.8.
* Note that the version string is set to 1.9.4.
*/
public WrappedServerPing() {
super(MinecraftReflection.getServerPingClass());
@@ -129,14 +117,8 @@ public class WrappedServerPing extends AbstractWrapper {
* Reset the version string to the default state.
*/
protected void resetVersion() {
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
MinecraftVersion minecraftVersion = LAST_VERSION;
// Fetch the latest known version
if (manager != null) {
minecraftVersion = manager.getMinecraftVersion();
}
version = VERSION_CONSTRUCTOR.invoke(minecraftVersion.toString(), VERSION_NUMBERS.get(minecraftVersion));
MinecraftVersion minecraftVersion = MinecraftVersion.getCurrentVersion();
version = VERSION_CONSTRUCTOR.invoke(minecraftVersion.toString(), MinecraftProtocolVersion.getCurrentVersion());
VERSION.set(handle, version);
}
@@ -132,24 +132,21 @@ public class WrappedSignedProperty extends AbstractWrapper {
}
@Override
public boolean equals(Object object){
public boolean equals(Object object) {
if (object == this) return true;
if (object instanceof WrappedSignedProperty) {
if (!super.equals(object))
return false;
WrappedSignedProperty that = (WrappedSignedProperty) object;
return Objects.equal(this.getName(), that.getName())
&& Objects.equal(this.getValue(), that.getValue())
&& Objects.equal(this.getSignature(), that.getSignature());
}
return false;
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("name", getName())
.add("value", getValue())
.add("signature", getSignature())
.toString();
return "WrappedSignedProperty[name=" + getName() + ", value=" + getValue() + ", signature=" + getSignature() + "]";
}
}
@@ -82,21 +82,4 @@ public class WrappedStatistic extends AbstractWrapper {
public String toString() {
return String.valueOf(handle);
}
@Override
public int hashCode() {
return handle.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof WrappedGameProfile) {
WrappedStatistic other = (WrappedStatistic) obj;
return handle.equals(other.handle);
}
return false;
}
}
@@ -16,27 +16,36 @@
*/
package com.comphenix.protocol.wrappers;
import static com.comphenix.protocol.utility.MinecraftReflection.is;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.base.Optional;
/**
* Represents a DataWatcher Item in 1.9.
* Represents a DataWatcher Item in 1.8 thru 1.10.
* @author dmulloy2
*/
public class WrappedWatchableObject extends AbstractWrapper {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherItemClass();
private static Integer VALUE_INDEX = null;
private static ConstructorAccessor constructor;
private final StructureModifier<Object> modifier;
/**
* Constructs a wrapped watchable object around an existing NMS data watcher item.
* Constructs a DataWatcher Item wrapper from an existing NMS data watcher item.
* @param handle Data watcher item
*/
public WrappedWatchableObject(Object handle) {
@@ -46,7 +55,18 @@ public class WrappedWatchableObject extends AbstractWrapper {
}
/**
* Constructs a wrapped watchable object with a given watcher object and initial value.
* Constructs a DataWatcher Item wrapper from a given index and initial value.
* <p>
* Not recommended in 1.9 and up.
* @param index Index of the Item
* @param value Initial value
*/
public WrappedWatchableObject(int index, Object value) {
this(newHandle(WrappedDataWatcherObject.fromIndex(index), value));
}
/**
* Constructs a DataWatcher Item wrapper from a given watcher object and initial value.
* @param watcherObject Watcher object
* @param value Initial value
*/
@@ -59,7 +79,12 @@ public class WrappedWatchableObject extends AbstractWrapper {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
}
return constructor.invoke(watcherObject.getHandle(), value);
if (MinecraftReflection.watcherObjectExists()) {
return constructor.invoke(watcherObject.getHandle(), value);
} else {
// new WatchableObject(classId, index, value)
return constructor.invoke(WrappedDataWatcher.getTypeID(value.getClass()), watcherObject.getIndex(), value);
}
}
// ---- Getter methods
@@ -77,7 +102,11 @@ public class WrappedWatchableObject extends AbstractWrapper {
* @return The index
*/
public int getIndex() {
return getWatcherObject().getIndex();
if (MinecraftReflection.watcherObjectExists()) {
return getWatcherObject().getIndex();
}
return modifier.<Integer>withType(int.class).read(1);
}
/**
@@ -93,44 +122,11 @@ public class WrappedWatchableObject extends AbstractWrapper {
* @return Raw value
*/
public Object getRawValue() {
return modifier.readSafely(1);
}
/**
* Retrieve the wrapped object value, if needed.
*
* @param value - the raw NMS object to wrap.
* @return The wrapped object.
*/
@SuppressWarnings("rawtypes")
static Object getWrapped(Object value) {
// Handle the special cases
if (MinecraftReflection.isItemStack(value)) {
return BukkitConverters.getItemStackConverter().getSpecific(value);
} else if (MinecraftReflection.isChunkCoordinates(value)) {
return new WrappedChunkCoordinate((Comparable) value);
} else {
return value;
if (VALUE_INDEX == null) {
VALUE_INDEX = MinecraftReflection.watcherObjectExists() ? 1 : 2;
}
}
/**
* Retrieve the wrapped type, if needed.
*
* @param wrapped - the wrapped class type.
* @return The wrapped class type.
*/
static Class<?> getWrappedType(Class<?> unwrapped) {
if (unwrapped.equals(MinecraftReflection.getChunkPositionClass()))
return ChunkPosition.class;
else if (unwrapped.equals(MinecraftReflection.getBlockPositionClass()))
return BlockPosition.class;
else if (unwrapped.equals(MinecraftReflection.getChunkCoordinatesClass()))
return WrappedChunkCoordinate.class;
else if (unwrapped.equals(MinecraftReflection.getItemStackClass()))
return ItemStack.class;
else
return unwrapped;
return modifier.readSafely(VALUE_INDEX);
}
/**
@@ -139,7 +135,11 @@ public class WrappedWatchableObject extends AbstractWrapper {
* @param updateClient Whether or not to update the client
*/
public void setValue(Object value, boolean updateClient) {
modifier.write(1, getUnwrapped(value));
if (VALUE_INDEX == null) {
VALUE_INDEX = MinecraftReflection.watcherObjectExists() ? 1 : 2;
}
modifier.write(VALUE_INDEX, getUnwrapped(value));
if (updateClient) {
setDirtyState(true);
@@ -154,51 +154,12 @@ public class WrappedWatchableObject extends AbstractWrapper {
setValue(value, false);
}
/**
* Retrieve the raw NMS value.
*
* @param wrapped - the wrapped position.
* @return The raw NMS object.
*/
static Object getUnwrapped(Object wrapped) {
// Convert special cases
if (wrapped instanceof ChunkPosition)
return ChunkPosition.getConverter().getGeneric(MinecraftReflection.getChunkPositionClass(), (ChunkPosition) wrapped);
else if (wrapped instanceof BlockPosition)
return BlockPosition.getConverter().getGeneric(MinecraftReflection.getBlockPositionClass(), (BlockPosition) wrapped);
else if (wrapped instanceof WrappedChunkCoordinate)
return ((WrappedChunkCoordinate) wrapped).getHandle();
else if (wrapped instanceof ItemStack)
return BukkitConverters.getItemStackConverter().getGeneric(MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
else
return wrapped;
}
/**
* Retrieve the unwrapped type, if needed.
*
* @param wrapped - the unwrapped class type.
* @return The unwrapped class type.
*/
static Class<?> getUnwrappedType(Class<?> wrapped) {
if (wrapped.equals(ChunkPosition.class))
return MinecraftReflection.getChunkPositionClass();
else if (wrapped.equals(BlockPosition.class))
return MinecraftReflection.getBlockPositionClass();
else if (wrapped.equals(WrappedChunkCoordinate.class))
return MinecraftReflection.getChunkCoordinatesClass();
else if (ItemStack.class.isAssignableFrom(wrapped))
return MinecraftReflection.getItemStackClass();
else
return wrapped;
}
/**
* Whether or not the value must be synchronized with the client.
* @return True if it must, false if not
*/
public boolean getDirtyState() {
return (boolean) modifier.read(2);
return modifier.<Boolean>withType(boolean.class).read(0);
}
/**
@@ -206,27 +167,132 @@ public class WrappedWatchableObject extends AbstractWrapper {
* @param dirty New state
*/
public void setDirtyState(boolean dirty) {
modifier.write(2, dirty);
modifier.<Boolean>withType(boolean.class).write(0, dirty);
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof WrappedWatchableObject) {
// watcher object, value, dirty state
WrappedWatchableObject other = (WrappedWatchableObject) obj;
return getWatcherObject().equals(other.getWatcherObject())
&& getRawValue().equals(other.getRawValue())
&& getDirtyState() == other.getDirtyState();
WrappedWatchableObject that = (WrappedWatchableObject) obj;
return this.getIndex() == that.getIndex() &&
this.getRawValue().equals(that.getRawValue()) &&
this.getDirtyState() == that.getDirtyState();
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getIndex();
result = prime * result + getRawValue().hashCode();
result = prime * result + (getDirtyState() ? 1231 : 1237);
return result;
}
@Override
public String toString() {
return "DataWatcherItem[object=" + getWatcherObject() + ", value=" + getValue() + ", dirty=" + getDirtyState() + "]";
return "DataWatcherItem[index=" + getIndex() + ", value=" + getValue() + ", dirty=" + getDirtyState() + "]";
}
// ---- Wrapping
/**
* Retrieve the wrapped object value, if needed. All non-primitive objects
* with {@link Serializer}s should be covered by this.
*
* @param value - the raw NMS object to wrap.
* @return The wrapped object.
*/
@SuppressWarnings("rawtypes")
static Object getWrapped(Object value) {
// Handle watcher items first
if (is(MinecraftReflection.getDataWatcherItemClass(), value)) {
return getWrapped(new WrappedWatchableObject(value).getRawValue());
}
// Then deal with optionals
if (value instanceof Optional) {
Optional<?> optional = (Optional<?>) value;
if (optional.isPresent()) {
return Optional.of(getWrapped(optional.get()));
} else {
return Optional.absent();
}
}
// Current supported classes
if (is(MinecraftReflection.getIChatBaseComponentClass(), value)) {
return WrappedChatComponent.fromHandle(value);
} else if (is(MinecraftReflection.getItemStackClass(), value)) {
return BukkitConverters.getItemStackConverter().getSpecific(value);
} else if (is(MinecraftReflection.getIBlockDataClass(), value)) {
return BukkitConverters.getWrappedBlockDataConverter().getSpecific(value);
} else if (is (Vector3F.getMinecraftClass(), value)) {
return Vector3F.getConverter().getSpecific(value);
} else if (is(MinecraftReflection.getBlockPositionClass(), value)) {
return BlockPosition.getConverter().getSpecific(value);
} else if (is(EnumWrappers.getDirectionClass(), value)) {
return EnumWrappers.getDirectionConverter().getSpecific(value);
} else if (is(MinecraftReflection.getNBTCompoundClass(), value)) {
return NbtFactory.fromNMSCompound(value);
}
// Legacy classes
if (is(MinecraftReflection.getChunkCoordinatesClass(), value)) {
return new WrappedChunkCoordinate((Comparable) value);
} else if (is(MinecraftReflection.getChunkPositionClass(), value)) {
return ChunkPosition.getConverter().getSpecific(value);
}
return value;
}
/**
* Retrieve the raw NMS value.
*
* @param wrapped - the wrapped position.
* @return The raw NMS object.
*/
// Must be kept in sync with getWrapped!
static Object getUnwrapped(Object wrapped) {
if (wrapped instanceof Optional) {
Optional<?> optional = (Optional<?>) wrapped;
if (optional.isPresent()) {
return Optional.of(getUnwrapped(optional.get()));
} else {
return Optional.absent();
}
}
// Current supported classes
if (wrapped instanceof WrappedChatComponent) {
return ((WrappedChatComponent) wrapped).getHandle();
} else if (wrapped instanceof ItemStack) {
return BukkitConverters.getItemStackConverter().getGeneric(MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
} else if (wrapped instanceof WrappedBlockData) {
return BukkitConverters.getWrappedBlockDataConverter().getGeneric(MinecraftReflection.getIBlockDataClass(), (WrappedBlockData) wrapped);
} else if (wrapped instanceof Vector3F) {
return Vector3F.getConverter().getGeneric(Vector3F.getMinecraftClass(), (Vector3F) wrapped);
} else if (wrapped instanceof BlockPosition) {
return BlockPosition.getConverter().getGeneric(MinecraftReflection.getBlockPositionClass(), (BlockPosition) wrapped);
} else if (wrapped instanceof Direction) {
return EnumWrappers.getDirectionConverter().getGeneric(EnumWrappers.getDirectionClass(), (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);
} else if (wrapped instanceof WrappedChunkCoordinate) {
return ((WrappedChunkCoordinate) wrapped).getHandle();
}
return wrapped;
}
}
@@ -133,7 +133,7 @@ public class NbtFactory {
* Set the NBT compound tag of a given item stack.
* <p>
* The item stack must be a wrapper for a CraftItemStack. Use
* {@link MinecraftReflection#getCraftItemStack(ItemStack)} if not.
* {@link MinecraftReflection#getBukkitItemStack(Object)} if not.
* @param stack - the item stack, cannot be air.
* @param compound - the new NBT compound, or NULL to remove it.
* @throws IllegalArgumentException If the stack is not a CraftItemStack, or it represents air.
@@ -151,7 +151,7 @@ public class NbtFactory {
* material, damage value or count.
* <p>
* The item stack must be a wrapper for a CraftItemStack. Use
* {@link MinecraftReflection#getCraftItemStack(ItemStack)} if not.
* {@link MinecraftReflection#getBukkitItemStack(Object)} if not.
* @param stack - the item stack.
* @return A wrapper for its NBT tag.
*/
@@ -46,7 +46,7 @@ class TileEntityAccessor<T extends BlockState> {
private boolean writeDetected;
private boolean readDetected;
private TileEntityAccessor() {
TileEntityAccessor() {
// Do nothing
}
@@ -59,27 +59,30 @@ class TileEntityAccessor<T extends BlockState> {
if (tileEntityField != null) {
this.tileEntityField = tileEntityField;
Class<?> type = tileEntityField.getField().getType();
// Possible read/write methods
try {
findMethodsUsingASM();
} catch (IOException ex1) {
try {
// Much slower though
findMethodUsingCGLib(state);
} catch (Exception ex2) {
throw new RuntimeException("Cannot find read/write methods in " + type, ex2);
}
}
// Ensure we found them
if (readCompound == null)
throw new RuntimeException("Unable to find read method in " + type);
if (writeCompound == null)
throw new RuntimeException("Unable to find write method in " + type);
findMethods(type, state);
}
}
void findMethods(Class<?> type, T state) {
// Possible read/write methods
try {
findMethodsUsingASM();
} catch (IOException ex1) {
try {
// Much slower though
findMethodUsingCGLib(state);
} catch (Exception ex2) {
throw new RuntimeException("Cannot find read/write methods in " + type, ex2);
}
}
// Ensure we found them
if (readCompound == null)
throw new RuntimeException("Unable to find read method in " + type);
if (writeCompound == null)
throw new RuntimeException("Unable to find write method in " + type);
}
/**
* Find the read/write methods in TileEntity.
* @throws IOException If we cannot find these methods.
@@ -90,7 +93,7 @@ class TileEntityAccessor<T extends BlockState> {
final ClassReader reader = new ClassReader(tileEntityClass.getCanonicalName());
final String tagCompoundName = getJarName(MinecraftReflection.getNBTCompoundClass());
final String expectedDesc = "(L" + tagCompoundName + ";)V";
final String expectedDesc = "(L" + tagCompoundName + ";)";
reader.accept(new EmptyClassVisitor() {
@Override
@@ -98,7 +101,7 @@ class TileEntityAccessor<T extends BlockState> {
final String methodName = name;
// Detect read/write calls to NBTTagCompound
if (expectedDesc.equals(desc)) {
if (desc.startsWith(expectedDesc)) {
return new EmptyMethodVisitor() {
private int readMethods;
private int writeMethods;
@@ -106,9 +109,9 @@ class TileEntityAccessor<T extends BlockState> {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
// This must be a virtual call on NBTTagCompound that accepts a String
if (opcode == Opcodes.INVOKEVIRTUAL &&
tagCompoundName.equals(owner) &&
desc.startsWith("(Ljava/lang/String")) {
if (opcode == Opcodes.INVOKEVIRTUAL
&& tagCompoundName.equals(owner)
&& desc.startsWith("(Ljava/lang/String")) {
// Is this a write call?
if (desc.endsWith(")V")) {
@@ -126,10 +129,12 @@ class TileEntityAccessor<T extends BlockState> {
} else if (writeMethods > readMethods) {
writeCompound = Accessors.getMethodAccessor(tileEntityClass, methodName, nbtCompoundClass);
}
super.visitEnd();
}
};
}
return null;
}
}, 0);
+16 -36
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>4.0.1</version>
<version>${minorVersion}</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
@@ -19,6 +19,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
@@ -62,6 +63,7 @@
<exclude>org.spigotmc:spigot</exclude>
<exclude>org.spigotmc:spigot-api</exclude>
<exclude>junit:junit</exclude>
<exclude>io.netty:*</exclude>
</excludes>
</artifactSet>
</configuration>
@@ -73,8 +75,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
@@ -87,11 +89,11 @@
<manifestEntries>
<Main-Class>com.comphenix.protocol.Application</Main-Class>
<Implementation-Title>ProtocolLib</Implementation-Title>
<Implementation-Version>${project.version}${project.build.number}</Implementation-Version>
<Implementation-Version>${project.fullVersion}</Implementation-Version>
<Implementation-Vendor>dmulloy2</Implementation-Vendor>
</manifestEntries>
</archive>
<finalName>ProtocolLib</finalName>
<finalName>${project.name}</finalName>
</configuration>
</plugin>
@@ -121,6 +123,7 @@
</activation>
<properties>
<project.build.number>-b${env.BUILD_NUMBER}</project.build.number>
<project.fullVersion>${project.version}${project.build.number}</project.fullVersion>
</properties>
</profile>
@@ -132,6 +135,9 @@
<value>true</value>
</property>
</activation>
<properties>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
<plugins>
<plugin>
@@ -162,6 +168,7 @@
</executions>
</plugin>
<!-- TODO: Look into signing releases
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
@@ -176,6 +183,7 @@
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
@@ -222,39 +230,11 @@
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-API</artifactId>
<version>4.0-SNAPSHOT</version>
</dependency>
<!-- Included with Minecraft
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
</dependency>
-->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.9.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.9.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.comphenix.executors</groupId>
<artifactId>BukkitExecutors</artifactId>
<version>1.1-SNAPSHOT</version>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<!-- Testing dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@@ -1,58 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import org.bukkit.Bukkit;
/**
* Ignore this class.
*
* @author Kristian
*/
public class Application {
private static Thread mainThread;
private static boolean primaryMethod = true;
public static void main(String[] args) {
// For now, though we might consider making a proper application
System.out.println("This is a Bukkit library. Place it in the plugin-folder and restart the server!");
}
/**
* Determine if we are running on the main thread.
* @return TRUE if we are, FALSE otherwise.
*/
public static boolean isPrimaryThread() {
if (primaryMethod) {
try {
return Bukkit.isPrimaryThread();
} catch (LinkageError e) {
primaryMethod = false;
}
}
// Fallback method
return Thread.currentThread().equals(mainThread);
}
/**
* Register the calling thread as the primary thread.
*/
static void registerPrimaryThread() {
mainThread = Thread.currentThread();
}
}
@@ -22,6 +22,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
@@ -252,16 +253,34 @@ class CommandProtocol extends CommandBase {
pw.println();
Set<PacketListener> listeners = manager.getPacketListeners();
if (listeners.size() > 0) {
Set<Plugin> plugins = new HashSet<>();
if (!listeners.isEmpty()) {
pw.println("Listeners:");
for (PacketListener listener : listeners) {
pw.println(DetailedErrorReporter.getStringDescription(listener));
Plugin plugin = listener.getPlugin();
if (plugin != null) {
plugins.add(plugin);
} else {
pw.println("(Missing plugin!)");
}
}
pw.println();
} else {
pw.println("No listeners");
}
if (!plugins.isEmpty()) {
pw.println("Plugins Using ProtocolLib:");
for (Plugin plugin : plugins) {
pw.println(plugin.getName() + " by " + plugin.getDescription().getAuthors());
}
}
sender.sendMessage("Data dump written to " + file.getAbsolutePath());
} catch (IOException ex) {
ProtocolLogger.log(Level.SEVERE, "Failed to create dump:", ex);
@@ -0,0 +1,268 @@
/**
* 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;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.*;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.google.common.base.Charsets;
import org.apache.commons.io.HexDump;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
/**
* Logs packets to a given stream
* @author dmulloy2
*/
public class PacketLogging implements CommandExecutor, PacketListener {
public static final String NAME = "packetlog";
private List<PacketType> sendingTypes = new ArrayList<>();
private List<PacketType> receivingTypes = new ArrayList<>();
private ListeningWhitelist sendingWhitelist;
private ListeningWhitelist receivingWhitelist;
private Logger fileLogger;
private LogLocation location = LogLocation.FILE;
private final ProtocolManager manager;
private final Plugin plugin;
PacketLogging(Plugin plugin, ProtocolManager manager) {
this.plugin = plugin;
this.manager = manager;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
PacketType type = null;
try {
if (args.length > 2) {
Protocol protocol;
try {
protocol = Protocol.valueOf(args[0].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown protocol " + args[0]);
return true;
}
Sender pSender;
try {
pSender = Sender.valueOf(args[1].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown sender: " + args[1]);
return true;
}
try {
try { // Try IDs first
int id = Integer.parseInt(args[2]);
type = PacketType.findCurrent(protocol, pSender, id);
} catch (NumberFormatException ex) { // Check packet names
String name = args[2];
for (PacketType packet : PacketType.values()) {
if (packet.getProtocol() == protocol && packet.getSender() == pSender) {
if (packet.name().equalsIgnoreCase(name)) {
type = packet;
break;
}
for (String className : packet.getClassNames()) {
if (className.equalsIgnoreCase(name)) {
type = packet;
break;
}
}
}
}
}
} catch (IllegalArgumentException ex) { // RIP
type = null;
}
if (type == null) {
sender.sendMessage(ChatColor.RED + "Unknown packet: " + args[2]);
return true;
}
if (args.length > 3) {
if (args[3].equalsIgnoreCase("console")) {
this.location = LogLocation.CONSOLE;
} else {
this.location = LogLocation.FILE;
}
}
if (pSender == Sender.CLIENT) {
if (receivingTypes.contains(type)) {
receivingTypes.remove(type);
} else {
receivingTypes.add(type);
}
} else {
if (sendingTypes.contains(type)) {
sendingTypes.remove(type);
} else {
sendingTypes.add(type);
}
}
startLogging();
sender.sendMessage(ChatColor.GREEN + "Now logging " + type.getPacketClass().getSimpleName());
return true;
}
sender.sendMessage(ChatColor.RED + "Invalid syntax: /packetlog <protocol> <sender> <packet> [location]");
return true;
} catch (Throwable ex) {
sender.sendMessage(ChatColor.RED + "Failed to parse command: " + ex.toString());
return true;
}
}
private void startLogging() {
manager.removePacketListener(this);
if (sendingTypes.isEmpty() && receivingTypes.isEmpty()) {
return;
}
this.sendingWhitelist = ListeningWhitelist.newBuilder().types(sendingTypes).build();
this.receivingWhitelist = ListeningWhitelist.newBuilder().types(receivingTypes).build();
// Setup the file logger if it hasn't been already
if (location == LogLocation.FILE && fileLogger == null) {
fileLogger = Logger.getLogger("ProtocolLib-FileLogging");
for (Handler handler : fileLogger.getHandlers())
fileLogger.removeHandler(handler);
fileLogger.setUseParentHandlers(false);
try {
File logFile = new File(plugin.getDataFolder(), "log.log");
FileHandler handler = new FileHandler(logFile.getAbsolutePath(), true);
handler.setFormatter(new LogFormatter());
fileLogger.addHandler(handler);
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to obtain log file:", ex);
return;
}
}
manager.addPacketListener(this);
}
@Override
public void onPacketSending(PacketEvent event) {
log(event);
}
@Override
public void onPacketReceiving(PacketEvent event) {
log(event);
}
// Here's where the magic happens
private static String hexDump(byte[] bytes) throws IOException {
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
HexDump.dump(bytes, 0, output, 0);
return new String(output.toByteArray(), Charsets.UTF_8);
}
}
private void log(PacketEvent event) {
try {
byte[] bytes = WirePacket.bytesFromPacket(event.getPacket());
String hexDump = hexDump(bytes);
if (location == LogLocation.FILE) {
fileLogger.log(Level.INFO, event.getPacketType() + ":");
fileLogger.log(Level.INFO, hexDump);
fileLogger.log(Level.INFO, "");
} else {
System.out.println(event.getPacketType() + ":");
System.out.println(hexDump);
System.out.println();
}
} catch (Throwable ex) {
plugin.getLogger().log(Level.WARNING, "Failed to log packet " + event.getPacketType() + ":", ex);
plugin.getLogger().log(Level.WARNING, "Clearing packet logger...");
sendingTypes.clear();
receivingTypes.clear();
startLogging();
}
}
@Override
public ListeningWhitelist getSendingWhitelist() {
return sendingWhitelist;
}
@Override
public ListeningWhitelist getReceivingWhitelist() {
return receivingWhitelist;
}
@Override
public Plugin getPlugin() {
return plugin;
}
private enum LogLocation {
CONSOLE, FILE
}
private static class LogFormatter extends Formatter {
private static final SimpleDateFormat DATE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static final String FORMAT = "[{0}] {1}";
@Override
public String format(LogRecord record) {
String string = formatMessage(record);
if (string.isEmpty()) {
return LINE_SEPARATOR;
}
StringBuilder message = new StringBuilder();
message.append(MessageFormat.format(FORMAT, DATE.format(record.getMillis()), string));
message.append(LINE_SEPARATOR);
return message.toString();
}
}
}
@@ -4,6 +4,7 @@ import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import com.comphenix.protocol.PacketType.Protocol;
@@ -53,7 +54,7 @@ class PacketTypeParser {
// And finally, parse packet names if we have a protocol
if (protocol != null) {
for (Iterator<String> it = arguments.iterator(); it.hasNext(); ) {
String name = it.next().toUpperCase();
String name = it.next().toUpperCase(Locale.ENGLISH);
Collection<PacketType> names = PacketType.fromName(name);
for (PacketType type : names) {
@@ -89,7 +89,8 @@ public class ProtocolLib extends JavaPlugin {
private enum ProtocolCommand {
FILTER,
PACKET,
PROTOCOL
PROTOCOL,
LOGGING;
}
/**
@@ -142,6 +143,7 @@ public class ProtocolLib extends JavaPlugin {
private CommandProtocol commandProtocol;
private CommandPacket commandPacket;
private CommandFilter commandFilter;
private PacketLogging packetLogging;
// Whether or not disable is not needed
private boolean skipDisable;
@@ -149,11 +151,9 @@ public class ProtocolLib extends JavaPlugin {
@Override
public void onLoad() {
// Logging
logger = getLoggerSafely();
logger = getLogger();
ProtocolLogger.init(this);
Application.registerPrimaryThread();
// Initialize enhancer factory
EnhancerFactory.getInstance().setClassLoader(getClassLoader());
@@ -237,8 +237,6 @@ public class ProtocolLib extends JavaPlugin {
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_PLUGIN_LOAD_ERROR).error(e).callerParam(protocolManager));
disablePlugin();
@@ -262,11 +260,12 @@ public class ProtocolLib extends JavaPlugin {
case PACKET:
commandPacket = new CommandPacket(reporter, this, logger, commandFilter, protocolManager);
break;
case LOGGING:
packetLogging = new PacketLogging(this, protocolManager);
break;
}
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (LinkageError e) {
logger.warning("Failed to register command " + command.name() + ": " + e);
} catch (Throwable e) {
@@ -397,6 +396,7 @@ public class ProtocolLib extends JavaPlugin {
registerCommand(CommandProtocol.NAME, commandProtocol);
registerCommand(CommandPacket.NAME, commandPacket);
registerCommand(CommandFilter.NAME, commandFilter);
registerCommand(PacketLogging.NAME, packetLogging);
// Player login and logout events
protocolManager.registerEvents(manager, this);
@@ -406,8 +406,6 @@ public class ProtocolLib extends JavaPlugin {
createPacketTask(server);
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_PLUGIN_ENABLE_ERROR).error(e));
disablePlugin();
@@ -421,8 +419,6 @@ public class ProtocolLib extends JavaPlugin {
}
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (IOException e) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_METRICS_IO_ERROR).error(e).callerParam(statistics));
} catch (Throwable e) {
@@ -489,7 +485,7 @@ public class ProtocolLib extends JavaPlugin {
File[] candidates = pluginFolder.listFiles();
if (candidates != null) {
for (File candidate : pluginFolder.listFiles()) {
for (File candidate : candidates) {
if (candidate.isFile() && !candidate.equals(loadedFile)) {
Matcher match = ourPlugin.matcher(candidate.getName());
if (match.matches()) {
@@ -572,8 +568,6 @@ public class ProtocolLib extends JavaPlugin {
}, ASYNC_MANAGER_DELAY, ASYNC_MANAGER_DELAY);
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
if (packetTask == -1) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_CREATE_TIMEOUT_TASK).error(e));
@@ -650,26 +644,6 @@ public class ProtocolLib extends JavaPlugin {
reporter = new BasicErrorReporter();
}
// Get the Bukkit logger first, before we try to create our own
private Logger getLoggerSafely() {
Logger log = null;
try {
log = getLogger();
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
// Ignore
}
// Use the default logger instead
if (log == null)
log = Logger.getLogger("Minecraft");
return log;
}
/**
* Retrieve the metrics instance used to measure users of this library.
* <p>
@@ -17,17 +17,15 @@
package com.comphenix.protocol.injector;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@@ -54,62 +52,44 @@ class EntityUtilities {
private static Method scanPlayersMethod;
/*
* While this function may look pretty bad, it's essentially just a reflection-warped
* version of the following:
*
* @SuppressWarnings("unchecked")
* public static void updateEntity2(Entity entity, List<Player> observers) {
*
* World world = entity.getWorld();
* WorldServer worldServer = ((CraftWorld) world).getHandle();
*
* EntityTracker tracker = worldServer.tracker;
* EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(entity.getEntityId());
*
* List<EntityPlayer> nmsPlayers = getNmsPlayers(observers);
*
* entry.trackedPlayers.removeAll(nmsPlayers);
* entry.scanPlayers(nmsPlayers);
* }
*
* private static List<EntityPlayer> getNmsPlayers(List<Player> players) {
* List<EntityPlayer> nsmPlayers = new ArrayList<EntityPlayer>();
*
* for (Player bukkitPlayer : players) {
* CraftPlayer craftPlayer = (CraftPlayer) bukkitPlayer;
* nsmPlayers.add(craftPlayer.getHandle());
* }
*
* return nsmPlayers;
* }
*
*/
public static void updateEntity2(Entity entity, List<Player> observers) {
EntityTrackerEntry entry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
List<EntityPlayer> nmsPlayers = getNmsPlayers(observers);
entry.trackedPlayers.removeAll(nmsPlayers);
entry.scanPlayers(nmsPlayers);
}
*/
public static void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
if (entity == null || !entity.isValid()) {
return;
}
try {
//EntityTrackerEntry trackEntity = (EntityTrackerEntry) tracker.trackedEntities.get(entity.getEntityId());
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
if (trackerEntry == null) {
throw new IllegalArgumentException("Cannot find entity trackers for " + entity + ".");
}
if (trackedPlayersField == null) {
// This one is fairly easy
trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*");
}
// Phew, finally there.
Collection<?> trackedPlayers = (Collection<?>) FieldUtils.readField(trackedPlayersField, trackerEntry, false);
Collection<?> trackedPlayers = getTrackedPlayers(trackedPlayersField, trackerEntry);
List<Object> nmsPlayers = unwrapBukkit(observers);
// trackEntity.trackedPlayers.clear();
trackedPlayers.removeAll(nmsPlayers);
// We have to rely on a NAME once again. Damn it.
// TODO: Make sure this stays up to date with version changes - 1.9
// TODO: Make sure this stays up to date with version changes - 1.8 - 1.10
if (scanPlayersMethod == null) {
scanPlayersMethod = trackerEntry.getClass().getMethod("scanPlayers", List.class);
}
//trackEntity.scanPlayers(server.players);
scanPlayersMethod.invoke(trackerEntry, nmsPlayers);
scanPlayersMethod.invoke(trackerEntry, nmsPlayers);
} catch (IllegalArgumentException e) {
throw e;
} catch (IllegalAccessException e) {
@@ -130,28 +110,23 @@ class EntityUtilities {
* @throws FieldAccessException If reflection failed.
*/
public static List<Player> getEntityTrackers(Entity entity) {
if (entity == null || !entity.isValid()) {
return new ArrayList<>();
}
try {
List<Player> result = new ArrayList<Player>();
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
if (trackerEntry == null) {
throw new IllegalArgumentException("Cannot find entity trackers for " + entity + (entity.isDead() ? " - entity is dead." : "."));
throw new IllegalArgumentException("Cannot find entity trackers for " + entity + ".");
}
if (trackedPlayersField == null) {
trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*");
}
Collection<?> trackedPlayers = null;
Object value = FieldUtils.readField(trackedPlayersField, trackerEntry, false);
if (value instanceof Collection) {
trackedPlayers = (Collection<?>) value;
} else if (value instanceof Map) { // PaperSpigot
trackedPlayers = ((Map<?, ?>) value).keySet();
} else { // Please, no more changes
throw new IllegalStateException("trackedPlayers field was an unknown type: expected Set or Map, but got " + value.getClass());
}
Collection<?> trackedPlayers = getTrackedPlayers(trackedPlayersField, trackerEntry);
// Wrap every player - we also ensure that the underlying tracker list is immutable
for (Object tracker : trackedPlayers) {
@@ -159,20 +134,38 @@ class EntityUtilities {
result.add((Player) MinecraftReflection.getBukkitEntity(tracker));
}
}
return result;
return result;
} catch (IllegalAccessException e) {
throw new FieldAccessException("Security limitation prevented access to the list of tracked players.", e);
}
}
/**
* Retrieve the entity tracker entry given a ID.
* @param world - world server.
* @param entityID - entity ID.
* @return The entity tracker entry.
* @throws FieldAccessException
*/
// Damn you, Paper
private static Collection<?> getTrackedPlayers(Field field, Object entry) throws IllegalAccessException {
Validate.notNull(field, "Cannot find 'trackedPlayers' field.");
Validate.notNull(entry, "entry cannot be null!");
Object value = FieldUtils.readField(field, entry, false);
if (value instanceof Collection) {
return (Collection<?>) value;
} else if (value instanceof Map) {
return ((Map<?, ?>) value).keySet();
} else {
// Please. No more changes.
throw new IllegalStateException("trackedPlayers field was an unknown type: expected Collection or Map, but got " + value.getClass());
}
}
/*
private static EntityTrackerEntry getEntityTrackerEntry2(World world, int entityID) {
WorldServer worldServer = ((CraftWorld) world).getHandle();
EntityTracker tracker = worldServer.tracker;
return tracker.trackedEntities.get(entityID);
}
*/
private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException {
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
Object worldServer = unwrapper.unwrapItem(world);
@@ -189,29 +182,18 @@ class EntityUtilities {
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot access 'tracker' field due to security limitations.", e);
}
// Looking for an IntHashMap in the tracker entry
if (trackedEntitiesField == null) {
@SuppressWarnings("rawtypes")
Set<Class> ignoredTypes = new HashSet<Class>();
// Well, this is more difficult. But we're looking for a Minecraft object that is not
// created by the constructor(s).
for (Constructor<?> constructor : tracker.getClass().getConstructors()) {
for (Class<?> type : constructor.getParameterTypes()) {
ignoredTypes.add(type);
}
}
// The Minecraft field that's NOT filled in by the constructor
trackedEntitiesField = FuzzyReflection.fromObject(tracker, true).
getFieldByType(MinecraftReflection.getMinecraftObjectRegex(), ignoredTypes);
trackedEntitiesField = FuzzyReflection.fromObject(tracker, false)
.getFieldByType("trackedEntities", MinecraftReflection.getIntHashMapClass());
}
// Read the entity hashmap
// Read the map
Object trackedEntities = null;
try {
trackedEntities = FieldUtils.readField(trackedEntitiesField, tracker, true);
trackedEntities = FieldUtils.readField(trackedEntitiesField, tracker, false);
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot access 'trackedEntities' field due to security limitations.", e);
}
@@ -221,7 +203,7 @@ class EntityUtilities {
/**
* Retrieve entity from a ID, even it it's newly created.
* @return The asssociated entity.
* @return The associated entity.
* @throws FieldAccessException Reflection error.
*/
public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException {
@@ -256,7 +238,6 @@ class EntityUtilities {
}
private static List<Object> unwrapBukkit(List<Player> players) {
List<Object> output = Lists.newArrayList();
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
@@ -17,22 +17,46 @@
package com.comphenix.protocol.injector;
import io.netty.channel.Channel;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.async.AsyncMarker;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.*;
import com.comphenix.protocol.injector.netty.ProtocolInjector;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
import com.comphenix.protocol.injector.packet.PacketInjector;
import com.comphenix.protocol.injector.packet.PacketInjectorBuilder;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.ConflictStrategy;
import com.comphenix.protocol.injector.player.PlayerInjector.ServerHandlerNull;
import com.comphenix.protocol.injector.player.PlayerInjectorBuilder;
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.Util;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import io.netty.channel.Channel;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@@ -50,46 +74,6 @@ import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.async.AsyncMarker;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.ListenerOptions;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.ProtocolInjector;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
import com.comphenix.protocol.injector.packet.PacketInjector;
import com.comphenix.protocol.injector.packet.PacketInjectorBuilder;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.ConflictStrategy;
import com.comphenix.protocol.injector.player.PlayerInjector.ServerHandlerNull;
import com.comphenix.protocol.injector.player.PlayerInjectorBuilder;
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.Util;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
public final class PacketFilterManager implements ListenerInvoker, InternalManager {
public static final ReportType REPORT_CANNOT_LOAD_PACKET_LIST = new ReportType("Cannot load server and client packet list.");
@@ -99,8 +83,8 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
new ReportType("%s doesn't depend on ProtocolLib. Check that its plugin.yml has a 'depend' directive.");
// Registering packet IDs that are not supported
public static final ReportType REPORT_UNSUPPORTED_SERVER_PACKET_ID = new ReportType("[%s] Unsupported server packet ID in current Minecraft version: %s");
public static final ReportType REPORT_UNSUPPORTED_CLIENT_PACKET_ID = new ReportType("[%s] Unsupported client packet ID in current Minecraft version: %s");
public static final ReportType REPORT_UNSUPPORTED_SERVER_PACKET = new ReportType("[%s] Unsupported server packet in current Minecraft version: %s");
public static final ReportType REPORT_UNSUPPORTED_CLIENT_PACKET = new ReportType("[%s] Unsupported client packet in current Minecraft version: %s");
// Problems injecting and uninjecting players
public static final ReportType REPORT_CANNOT_UNINJECT_PLAYER = new ReportType("Unable to uninject net handler for player.");
@@ -226,8 +210,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
this.pluginVerifier = new PluginVerifier(builder.getLibrary());
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
reporter.reportWarning(this, Report.newBuilder(REPORT_PLUGIN_VERIFIER_ERROR).
messageParam(e.getMessage()).error(e));
@@ -453,9 +435,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
// We only check the recieving whitelist
if (whitelist.getOptions().contains(ListenerOptions.INTERCEPT_INPUT_BUFFER)) {
for (PacketType type : whitelist.getTypes()) {
updated.add(type);
}
updated.addAll(whitelist.getTypes());
}
}
// Update it
@@ -630,7 +610,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
* Note that all packets are disabled by default.
*
* @param listener - the listener that requested to enable these filters.
* @param side - which side the event will arrive from.
* @param packets - the packet id(s).
*/
private void enablePacketFilters(PacketListener listener, Iterable<PacketType> packets) {
@@ -645,7 +624,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
playerInjection.addPacketHandler(type, listener.getSendingWhitelist().getOptions());
else
reporter.reportWarning(this,
Report.newBuilder(REPORT_UNSUPPORTED_SERVER_PACKET_ID).messageParam(PacketAdapter.getPluginName(listener), type)
Report.newBuilder(REPORT_UNSUPPORTED_SERVER_PACKET).messageParam(PacketAdapter.getPluginName(listener), type)
);
}
@@ -655,7 +634,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
packetInjector.addPacketHandler(type, listener.getReceivingWhitelist().getOptions());
else
reporter.reportWarning(this,
Report.newBuilder(REPORT_UNSUPPORTED_CLIENT_PACKET_ID).messageParam(PacketAdapter.getPluginName(listener), type)
Report.newBuilder(REPORT_UNSUPPORTED_CLIENT_PACKET).messageParam(PacketAdapter.getPluginName(listener), type)
);
}
}
@@ -781,7 +760,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
if (!filters) {
// We may have to delay the packet due to non-asynchronous monitor listeners
if (!filters && !Bukkit.isPrimaryThread() && playerInjection.hasMainThreadListener(packet.getType())) {
if (!Bukkit.isPrimaryThread() && playerInjection.hasMainThreadListener(packet.getType())) {
final NetworkMarker copy = marker;
server.getScheduler().scheduleSyncDelayedTask(library, new Runnable() {
@@ -990,40 +969,34 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
if (nettyInjector != null)
nettyInjector.inject();
try {
manager.registerEvents(new Listener() {
manager.registerEvents(new Listener() {
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent event) {
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent event) {
PacketFilterManager.this.onPlayerLogin(event);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPrePlayerJoin(PlayerJoinEvent event) {
@EventHandler(priority = EventPriority.LOWEST)
public void onPrePlayerJoin(PlayerJoinEvent event) {
PacketFilterManager.this.onPrePlayerJoin(event);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerJoin(PlayerJoinEvent event) {
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerJoin(PlayerJoinEvent event) {
PacketFilterManager.this.onPlayerJoin(event);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
PacketFilterManager.this.onPlayerQuit(event);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPluginDisabled(PluginDisableEvent event) {
@EventHandler(priority = EventPriority.MONITOR)
public void onPluginDisabled(PluginDisableEvent event) {
PacketFilterManager.this.onPluginDisabled(event, plugin);
}
}, plugin);
} catch (NoSuchMethodError e) {
// Oh wow! We're running on 1.0.0 or older.
registerOld(manager, plugin);
}
}, plugin);
}
private void onPlayerLogin(PlayerLoginEvent event) {
@@ -1132,113 +1105,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
return PacketRegistry.getPacketClassFromID(packetID, forceVanilla);
}
// Yes, this is crazy.
@SuppressWarnings({ "unchecked", "rawtypes" })
private void registerOld(PluginManager manager, final Plugin plugin) {
try {
ClassLoader loader = manager.getClass().getClassLoader();
// The different enums we are going to need
Class eventTypes = loader.loadClass("org.bukkit.event.Event$Type");
Class eventPriority = loader.loadClass("org.bukkit.event.Event$Priority");
// Get the priority
Object priorityLowest = Enum.valueOf(eventPriority, "Lowest");
Object priorityMonitor = Enum.valueOf(eventPriority, "Monitor");
// Get event types
Object playerJoinType = Enum.valueOf(eventTypes, "PLAYER_JOIN");
Object playerQuitType = Enum.valueOf(eventTypes, "PLAYER_QUIT");
Object pluginDisabledType = Enum.valueOf(eventTypes, "PLUGIN_DISABLE");
// The player listener! Good times.
Class<?> playerListener = loader.loadClass("org.bukkit.event.player.PlayerListener");
Class<?> serverListener = loader.loadClass("org.bukkit.event.server.ServerListener");
// Find the register event method
Method registerEvent = FuzzyReflection.fromObject(manager).getMethodByParameters("registerEvent",
eventTypes, Listener.class, eventPriority, Plugin.class);
Enhancer playerLow = EnhancerFactory.getInstance().createEnhancer();
Enhancer playerEx = EnhancerFactory.getInstance().createEnhancer();
Enhancer serverEx = EnhancerFactory.getInstance().createEnhancer();
playerLow.setSuperclass(playerListener);
playerLow.setClassLoader(classLoader);
playerLow.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
// Must have a parameter
if (args.length == 1) {
Object event = args[0];
if (event instanceof PlayerJoinEvent) {
onPrePlayerJoin((PlayerJoinEvent) event);
}
}
return null;
}
});
playerEx.setSuperclass(playerListener);
playerEx.setClassLoader(classLoader);
playerEx.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (args.length == 1) {
Object event = args[0];
// Check for the correct event
if (event instanceof PlayerJoinEvent) {
onPlayerJoin((PlayerJoinEvent) event);
} else if (event instanceof PlayerQuitEvent) {
onPlayerQuit((PlayerQuitEvent) event);
}
}
return null;
}
});
serverEx.setSuperclass(serverListener);
serverEx.setClassLoader(classLoader);
serverEx.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// Must have a parameter
if (args.length == 1) {
Object event = args[0];
if (event instanceof PluginDisableEvent)
onPluginDisabled((PluginDisableEvent) event, plugin);
}
return null;
}
});
// Create our listener
Object playerProxyLow = playerLow.create();
Object playerProxy = playerEx.create();
Object serverProxy = serverEx.create();
registerEvent.invoke(manager, playerJoinType, playerProxyLow, priorityLowest, plugin);
registerEvent.invoke(manager, playerJoinType, playerProxy, priorityMonitor, plugin);
registerEvent.invoke(manager, playerQuitType, playerProxy, priorityMonitor, plugin);
registerEvent.invoke(manager, pluginDisabledType, serverProxy, priorityMonitor, plugin);
// A lot can go wrong
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* Retrieve every known and supported server packet.
* @return An immutable set of every known server packet.
@@ -16,19 +16,6 @@
*/
package com.comphenix.protocol.injector.netty;
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.socket.SocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.internal.TypeParameterMatcher;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.net.SocketAddress;
@@ -55,7 +42,6 @@ import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.NetworkProcessor;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.injector.server.SocketInjector;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.VolatileField;
@@ -70,6 +56,20 @@ 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.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;
/**
* Represents a channel injector.
* @author Kristian
@@ -90,6 +90,10 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
private static Class<?> PACKET_LOGIN_CLIENT = null;
private static FieldAccessor LOGIN_GAME_PROFILE = null;
// Versioning
private static Class<?> PACKET_SET_PROTOCOL = null;
private static AttributeKey<Integer> PROTOCOL_KEY = AttributeKey.valueOf("PROTOCOL");
// Saved accessors
private static MethodAccessor DECODE_BUFFER;
private static MethodAccessor ENCODE_BUFFER;
@@ -98,9 +102,6 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// For retrieving the protocol
private static FieldAccessor PROTOCOL_ACCESSOR;
// For retrieving the protocol version
private static MethodAccessor PROTOCOL_VERSION;
// The factory that created this injector
private InjectionFactory factory;
@@ -185,19 +186,8 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
*/
@Override
public int getProtocolVersion() {
MethodAccessor accessor = PROTOCOL_VERSION;
if (accessor == null) {
try {
accessor = Accessors.getMethodAccessor(networkManager.getClass(), "getVersion");
} catch (Throwable ex) {
}
}
if (accessor != null) {
return (Integer) accessor.invoke(networkManager);
} else {
return MinecraftProtocolVersion.getCurrentVersion();
}
Integer value = originalChannel.attr(PROTOCOL_KEY).get();
return value != null ? value : MinecraftProtocolVersion.getCurrentVersion();
}
@Override
@@ -582,35 +572,44 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param packet - the packet.
*/
protected void handleLogin(Class<?> packetClass, Object packet) {
try {
Class<?> loginClass = PACKET_LOGIN_CLIENT;
FieldAccessor loginClient = LOGIN_GAME_PROFILE;
// Try to find the login packet class
if (PACKET_LOGIN_CLIENT == null) {
PACKET_LOGIN_CLIENT = PacketType.Login.Client.START.getPacketClass();
}
// Initialize packet class and login
if (loginClass == null) {
loginClass = PacketType.Login.Client.START.getPacketClass();
PACKET_LOGIN_CLIENT = loginClass;
}
if (loginClient == null) {
loginClient = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, MinecraftReflection.getGameProfileClass(), true);
LOGIN_GAME_PROFILE = loginClient;
}
// If we can't, there's an issue
if (PACKET_LOGIN_CLIENT == null) {
throw new IllegalStateException("Failed to obtain login start packet. Did you build Spigot with BuildTools?");
}
// See if we are dealing with the login packet
if (loginClass.equals(packetClass)) {
// GameProfile profile = (GameProfile) loginClient.get(packet);
WrappedGameProfile profile = WrappedGameProfile.fromHandle(loginClient.get(packet));
if (LOGIN_GAME_PROFILE == null) {
LOGIN_GAME_PROFILE = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, MinecraftReflection.getGameProfileClass(), true);
}
// Save the channel injector
factory.cacheInjector(profile.getName(), this);
// See if we are dealing with the login packet
if (PACKET_LOGIN_CLIENT.equals(packetClass)) {
WrappedGameProfile profile = WrappedGameProfile.fromHandle(LOGIN_GAME_PROFILE.get(packet));
// Save the channel injector
factory.cacheInjector(profile.getName(), this);
}
if (PACKET_SET_PROTOCOL == null) {
try {
PACKET_SET_PROTOCOL = PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass();
} catch (Throwable ex) {
PACKET_SET_PROTOCOL = getClass(); // If we can't find it don't worry about it
}
}
if (PACKET_SET_PROTOCOL.equals(packetClass)) {
FuzzyReflection fuzzy = FuzzyReflection.fromObject(packet);
try {
int protocol = (int) fuzzy.invokeMethod(packet, "getProtocol", int.class);
originalChannel.attr(PROTOCOL_KEY).set(protocol);
} catch (Throwable ex) {
// Oh well
}
} catch (IllegalArgumentException ex) { // Thrown by FuzzyReflection#getFields()
System.err.println(String.format("[ProtocolLib] Encountered NPE in handleLogin(%s, %s)", packetClass, packet));
System.err.println("PACKET_LOGIN_CLIENT = " + PACKET_LOGIN_CLIENT);
System.err.println("LOGIN_GAME_PROFILE = " + LOGIN_GAME_PROFILE);
System.err.println("GameProfile class = " + MinecraftReflection.getGameProfileClass());
System.err.println("Provide this information in a new or existing issue");
throw ex;
}
}
@@ -680,7 +679,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
MinecraftMethods.getSendPacketMethod().invoke(getPlayerConnection(), packet);
}
} catch (Throwable ex) {
ProtocolLibrary.getErrorReporter().reportWarning(factory.getPlugin(),
ProtocolLibrary.getErrorReporter().reportWarning(this,
Report.newBuilder(REPORT_CANNOT_SEND_PACKET).messageParam(packet, playerName).error(ex).build());
}
}
@@ -725,7 +724,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
*/
private Object getPlayerConnection() {
if (playerConnection == null) {
playerConnection = MinecraftFields.getPlayerConnection(player);
playerConnection = MinecraftFields.getPlayerConnection(getPlayer());
}
return playerConnection;
}
@@ -16,6 +16,15 @@
*/
package com.comphenix.protocol.injector.netty;
import java.lang.reflect.Field;
import java.net.SocketAddress;
import java.util.Map;
import java.util.concurrent.Callable;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.google.common.collect.Maps;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
@@ -28,15 +37,6 @@ import io.netty.channel.EventLoop;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import java.lang.reflect.Field;
import java.net.SocketAddress;
import java.util.Map;
import java.util.concurrent.Callable;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.google.common.collect.Maps;
public abstract class ChannelProxy implements Channel {
// Mark that a certain object does not contain a message field
private static final FieldAccessor MARK_NO_MESSAGE = new FieldAccessor() {
@@ -331,4 +331,22 @@ public abstract class ChannelProxy implements Channel {
public int compareTo(Channel o) {
return delegate.compareTo(o);
}
/* Added in Netty 4.1, seem to be unused
public long bytesBeforeUnwritable() {
return delegate.bytesBeforeUnwritable();
}
public long bytesBeforeWritable() {
return delegate.bytesBeforeWritable();
}
public ChannelId id() {
return delegate.id();
}
public <T> boolean hasAttr(AttributeKey<T> key) {
return delegate.hasAttr(key);
}
*/
}
@@ -256,4 +256,10 @@ abstract class EventLoopProxy implements EventLoop {
public List<Runnable> shutdownNow() {
return getDelegate().shutdownNow();
}
/*
public ChannelFuture register(ChannelPromise promise) {
return getDelegate().register(promise);
}
*/
}
@@ -16,6 +16,12 @@
*/
package com.comphenix.protocol.injector.netty;
import java.net.SocketAddress;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
@@ -24,12 +30,6 @@ import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.util.concurrent.EventExecutorGroup;
import java.net.SocketAddress;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* A pipeline proxy.
* @author Kristian
@@ -368,4 +368,26 @@ public class PipelineProxy implements ChannelPipeline {
public ChannelFuture writeAndFlush(Object arg0) {
return pipeline.writeAndFlush(arg0);
}
/* Added in Netty 4.1, seem to be unused
public ChannelFuture newFailedFuture(Throwable ex) {
return pipeline.newFailedFuture(ex);
}
public ChannelProgressivePromise newProgressivePromise() {
return pipeline.newProgressivePromise();
}
public ChannelPromise newPromise() {
return pipeline.newPromise();
}
public ChannelFuture newSucceededFuture() {
return pipeline.newSucceededFuture();
}
public ChannelPromise voidPromise() {
return pipeline.voidPromise();
}
*/
}
@@ -16,18 +16,11 @@
*/
package com.comphenix.protocol.injector.netty;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Set;
@@ -36,6 +29,7 @@ import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.concurrency.PacketTypeSet;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
@@ -55,8 +49,17 @@ import com.comphenix.protocol.injector.spigot.AbstractPlayerHandler;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.Lists;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
public class ProtocolInjector implements ChannelListener {
public static final ReportType REPORT_CANNOT_INJECT_INCOMING_CHANNEL = new ReportType("Unable to inject incoming channel %s.");
@@ -130,23 +133,32 @@ public class ProtocolInjector implements ChannelListener {
if (serverConnection != null) {
break;
}
} catch (Exception e) {
// Try the next though
e.printStackTrace();
} catch (Exception ex) {
ProtocolLogger.debug("Encountered an exception invoking " + method, ex);
}
}
if (serverConnection == null) {
throw new ReflectiveOperationException("Failed to obtain server connection");
}
// Handle connected channels
final ChannelInboundHandler endInitProtocol = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
protected void initChannel(final Channel channel) throws Exception {
try {
// This can take a while, so we need to stop the main thread from interfering
synchronized (networkManagers) {
injectionFactory.fromChannel(channel, ProtocolInjector.this, playerFactory).inject();
// For some reason it needs to be delayed on 1.12, but the delay breaks 1.11 and below
// TODO I see this more as a temporary hotfix than a permanent solution
if (MinecraftVersion.getCurrentVersion().getMinor() >= 12) {
channel.eventLoop().submit(() ->
injectionFactory.fromChannel(channel, ProtocolInjector.this, playerFactory).inject());
} else {
injectionFactory.fromChannel(channel, ProtocolInjector.this, playerFactory).inject();
}
}
} catch (Exception e) {
reporter.reportDetailed(ProtocolInjector.this, Report.newBuilder(REPORT_CANNOT_INJECT_INCOMING_CHANNEL).messageParam(channel).error(e));
} catch (Exception ex) {
reporter.reportDetailed(ProtocolInjector.this, Report.newBuilder(REPORT_CANNOT_INJECT_INCOMING_CHANNEL).messageParam(channel).error(ex));
}
}
};
@@ -171,9 +183,27 @@ public class ProtocolInjector implements ChannelListener {
ctx.fireChannelRead(msg);
}
};
FuzzyReflection fuzzy = FuzzyReflection.fromObject(serverConnection, true);
// Get the current NetworkMananger list
networkManagers = (List<Object>) FuzzyReflection.fromObject(serverConnection, true).invokeMethod(null, "getNetworkManagers", List.class, serverConnection);
try {
Field field = fuzzy.getParameterizedField(List.class, MinecraftReflection.getNetworkManagerClass());
field.setAccessible(true);
networkManagers = (List<Object>) field.get(serverConnection);
} catch (Exception ex) {
ProtocolLogger.debug("Encountered an exception checking list fields", ex);
Method method = fuzzy.getMethodByParameters("getNetworkManagers", List.class,
new Class<?>[] { serverConnection.getClass() });
method.setAccessible(true);
networkManagers = (List<Object>) method.invoke(null, serverConnection);
}
if (networkManagers == null) {
throw new ReflectiveOperationException("Failed to obtain list of network managers");
}
// Insert ProtocolLib's connection interceptor
bootstrapFields = getBootstrapFields(serverConnection);
@@ -191,7 +221,6 @@ public class ProtocolInjector implements ChannelListener {
}
injected = true;
} catch (Exception e) {
throw new RuntimeException("Unable to inject channel futures.", e);
}
@@ -352,7 +381,7 @@ public class ProtocolInjector implements ChannelListener {
@Override
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
if (options != null && !type.forceAsync() && !options.contains(ListenerOptions.ASYNC))
if (!type.isAsyncForced() && (options == null || !options.contains(ListenerOptions.ASYNC)))
mainThreadFilters.addType(type);
super.addPacketHandler(type, options);
}
@@ -27,34 +27,24 @@
*/
package com.comphenix.protocol.metrics;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.io.*;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
import com.comphenix.protocol.utility.Util;
import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import com.comphenix.protocol.utility.Util;
import com.comphenix.protocol.utility.WrappedScheduler;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
public class Metrics {
@@ -116,7 +106,7 @@ public class Metrics {
/**
* The scheduled task
*/
private volatile WrappedScheduler.TaskWrapper task = null;
private volatile BukkitTask task;
public Metrics(final Plugin plugin) throws IOException {
if (plugin == null) {
@@ -200,8 +190,7 @@ public class Metrics {
}
// Begin hitting the server with glorious data
task = WrappedScheduler.runAsynchronouslyRepeat(plugin, new Runnable() {
task = new BukkitRunnable() {
private boolean firstPost = true;
@Override
@@ -234,8 +223,7 @@ public class Metrics {
}
}
}
}, 0, PING_INTERVAL * 1200);
}.runTaskTimerAsynchronously(plugin, 0, PING_INTERVAL * 1200);
return true;
}
}
@@ -354,11 +354,12 @@ public class BukkitUpdater extends Updater {
this.plugin.getLogger().warning("Please double-check your configuration to ensure it is correct.");
this.result = UpdateResult.FAIL_APIKEY;
} else {
this.plugin.getLogger().warning("The updater could not contact dev.bukkit.org for updating.");
this.plugin.getLogger().warning("If you have not recently modified your configuration and this is the first time you are seeing this message, the site may be experiencing temporary downtime.");
// People don't care
// this.plugin.getLogger().warning("The updater could not contact dev.bukkit.org for updating.");
// this.plugin.getLogger().warning("If you have not recently modified your configuration and this is the first time you are seeing this message, the site may be experiencing temporary downtime.");
this.result = UpdateResult.FAIL_DBO;
}
e.printStackTrace();
// e.printStackTrace();
return false;
}
}
@@ -21,7 +21,6 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.logging.Level;
import org.bukkit.plugin.Plugin;
@@ -75,7 +74,8 @@ public final class SpigotUpdater extends Updater {
ProtocolLibrary.getErrorReporter().reportDetailed(
SpigotUpdater.this, Report.newBuilder(REPORT_CANNOT_UPDATE_PLUGIN).error(ex).callerParam(this));
} else {
plugin.getLogger().log(Level.WARNING, "Failed to check for updates: " + ex);
// People don't care
// plugin.getLogger().log(Level.WARNING, "Failed to check for updates: " + ex);
}
ProtocolLibrary.disableUpdates();
@@ -1,6 +1,5 @@
name: ProtocolLib
#version: ${project.version}${project.build.number}
version: ${project.version}
version: ${project.fullVersion}
description: Provides read/write access to the Minecraft protocol.
authors: [dmulloy2, comphenix]
@@ -25,6 +24,11 @@ commands:
aliases: [packet_filter]
permission: protocol.admin
permission-message: You don't have <permission>
packetlog:
description: Logs hex representations of packets to a file or console
usage: /<command> <protocol> <sender> <packet> [location]
permission: protocol.admin
permission-message: You don't have <permission>
permissions:
protocol.*:
@@ -5,13 +5,13 @@ import static org.mockito.Mockito.when;
import java.util.logging.Logger;
import net.minecraft.server.v1_9_R2.DispenserRegistry;
import net.minecraft.server.v1_12_R1.DispenserRegistry;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_9_R2.CraftServer;
import org.bukkit.craftbukkit.v1_9_R2.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_9_R2.util.Versioning;
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;
@@ -24,6 +24,7 @@ import com.comphenix.protocol.utility.MinecraftVersion;
*/
public class BukkitInitialization {
private static boolean initialized;
private static boolean packaged;
/**
* Initialize Bukkit and ProtocolLib such that we can perfrom unit testing.
@@ -34,6 +35,8 @@ public class BukkitInitialization {
// Denote that we're done
initialized = true;
initializePackage();
DispenserRegistry.c(); // Basically registers everything
// Mock the server object
@@ -49,8 +52,6 @@ public class BukkitInitialization {
// Inject this fake server
Bukkit.setServer(mockedServer);
initializePackage();
}
}
@@ -58,8 +59,11 @@ public class BukkitInitialization {
* Ensure that package names are correctly set up.
*/
public static void initializePackage() {
// Initialize reflection
MinecraftReflection.setMinecraftPackage(Constants.NMS, Constants.OBC);
MinecraftVersion.setCurrentVersion(MinecraftVersion.COMBAT_UPDATE);
if (!packaged) {
packaged = true;
MinecraftReflection.setMinecraftPackage(Constants.NMS, Constants.OBC);
MinecraftVersion.setCurrentVersion(MinecraftVersion.COLOR_UPDATE);
}
}
}
@@ -1,21 +1,42 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 dmulloy2
* <p>
* 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.
* <p>
* 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.
* <p>
* 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
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 org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.injector.netty.NettyProtocolRegistry;
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
import net.minecraft.server.v1_9_R2.PacketLoginInStart;
import static org.junit.Assert.*;
/**
* @author dmulloy2
*/
public class PacketTypeTest {
@BeforeClass
@@ -35,22 +56,41 @@ public class PacketTypeTest {
}
@Test
public void ensureAllExist() {
boolean missing = false;
ProtocolRegistry registry = new NettyProtocolRegistry();
Map<PacketType, Class<?>> lookup = registry.getPacketTypeLookup();
for (Entry<PacketType, Class<?>> entry : lookup.entrySet()) {
PacketType type = entry.getKey();
Class<?> clazz = entry.getValue();
public void testDeprecation() {
assertTrue("Packet isn't properly deprecated", PacketType.Status.Server.OUT_SERVER_INFO.isDeprecated());
assertTrue("Deprecated packet isn't properly included",
PacketRegistry.getServerPacketTypes().contains(PacketType.Status.Server.OUT_SERVER_INFO));
assertFalse("Packet isn't properly deprecated", PacketType.Play.Server.CHAT.isDeprecated());
assertEquals("Deprecated packets aren't equal", PacketType.Status.Server.OUT_SERVER_INFO,
PacketType.Status.Server.SERVER_INFO);
}
if (type.isDynamic()) {
System.err.println("Packet " + clazz + " does not have a corresponding PacketType!");
missing = true;
@Test
@SuppressWarnings("unchecked")
public void ensureTypesAreCorrect() throws Exception {
boolean fail = false;
EnumProtocol[] protocols = EnumProtocol.values();
for (EnumProtocol protocol : protocols) {
Field field = EnumProtocol.class.getDeclaredField("h");
field.setAccessible(true);
Map<EnumProtocolDirection, Map<Integer, Class<?>>> map = (Map<EnumProtocolDirection, Map<Integer, Class<?>>>) field.get(protocol);
for (Entry<EnumProtocolDirection, Map<Integer, Class<?>>> entry : map.entrySet()) {
Map<Integer, Class<?>> treeMap = new TreeMap<>(entry.getValue());
for (Entry<Integer, Class<?>> entry1 : treeMap.entrySet()) {
try {
PacketType type = PacketType.fromClass(entry1.getValue());
if (type.getCurrentId() != entry1.getKey())
throw new IllegalStateException("Packet ID for " + type + " is incorrect. Expected " + entry1.getKey() + ", but got " + type.getCurrentId());
} catch (Throwable ex) {
ex.printStackTrace();
fail = true;
}
}
}
//assertFalse("Packet " + clazz + " does not have a corresponding PacketType!", type.isDynamic());
}
assertFalse("There are packets that aren\'t accounted for!", missing);
assertTrue("Packet type(s) were incorrect!", !fail);
}
}
@@ -24,19 +24,20 @@ 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 net.minecraft.server.v1_9_R2.AttributeModifier;
import net.minecraft.server.v1_9_R2.DataWatcher;
import net.minecraft.server.v1_9_R2.Entity;
import net.minecraft.server.v1_9_R2.EntityLightning;
import net.minecraft.server.v1_9_R2.MobEffect;
import net.minecraft.server.v1_9_R2.MobEffectList;
import net.minecraft.server.v1_9_R2.PacketPlayOutBoss;
import net.minecraft.server.v1_9_R2.PacketPlayOutUpdateAttributes;
import net.minecraft.server.v1_9_R2.PacketPlayOutUpdateAttributes.AttributeSnapshot;
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 org.apache.commons.lang.SerializationUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
@@ -203,7 +204,7 @@ public class PacketContainerTest {
ItemStack item = itemWithData();
StructureModifier<ItemStack> items = windowClick.getItemModifier();
assertNull(items.read(0));
// assertNull(items.read(0));
// Insert the item and check if it's there
items.write(0, item);
@@ -220,31 +221,22 @@ public class PacketContainerTest {
}
@Test
public void testGetItemArrayModifier() {
public void testGetItemListModifier() {
PacketContainer windowItems = new PacketContainer(PacketType.Play.Server.WINDOW_ITEMS);
StructureModifier<ItemStack[]> itemAccess = windowItems.getItemArrayModifier();
StructureModifier<List<ItemStack>> itemAccess = windowItems.getItemListModifier();
ItemStack[] itemArray = new ItemStack[] {
itemWithData(),
new ItemStack(Material.DIAMOND_AXE)
};
List<ItemStack> items = new ArrayList<>();
items.add(itemWithData());
items.add(new ItemStack(Material.DIAMOND_AXE));
assertNull(itemAccess.read(0));
// Insert and check that it was succesful
itemAccess.write(0, itemArray);
itemAccess.write(0, items);
// Read back array
ItemStack[] comparison = itemAccess.read(0);
assertEquals(itemArray.length, comparison.length);
// Check that it is equivalent
for (int i = 0; i < itemArray.length; i++) {
ItemStack original = itemArray[i];
ItemStack written = comparison[i];
assertTrue(String.format("Array element %s is not the same: %s != %s", i, original, written), equivalentItem(original, written));
}
List<ItemStack> comparison = itemAccess.read(0);
assertEquals(items, comparison);
}
private boolean equivalentItem(ItemStack first, ItemStack second) {
@@ -520,6 +512,19 @@ public class PacketContainerTest {
UPDATE_PROPERTIES;
}
@Test
public void testComponentArrays() {
PacketContainer signChange = new PacketContainer(PacketType.Play.Server.TILE_ENTITY_DATA);
WrappedChatComponent[] components = new WrappedChatComponent[] {
WrappedChatComponent.fromText("hello world"), WrappedChatComponent.fromText(""),
WrappedChatComponent.fromText(""), WrappedChatComponent.fromText("")
};
signChange.getChatComponentArrays().write(0, components);
WrappedChatComponent[] back = signChange.getChatComponentArrays().read(0);
assertArrayEquals(components, back);
}
private static final List<PacketType> BLACKLISTED = Util.asList(
PacketType.Play.Client.CUSTOM_PAYLOAD, PacketType.Play.Server.CUSTOM_PAYLOAD,
PacketType.Play.Server.SET_COOLDOWN
@@ -529,7 +534,7 @@ public class PacketContainerTest {
public void testDeepClone() {
// Try constructing all the packets
for (PacketType type : PacketType.values()) {
if (BLACKLISTED.contains(type)) {
if (BLACKLISTED.contains(type) || type.isDeprecated()) {
continue;
}
@@ -540,7 +545,7 @@ public class PacketContainerTest {
PacketContainer constructed = new PacketContainer(type);
if (!registered) {
fail("Expected IllegalArgumentException(Packet " + type + " not registered");
fail("Expected IllegalArgumentException(Packet " + type + " not registered)");
}
// Initialize default values
@@ -575,24 +580,17 @@ public class PacketContainerTest {
}
} catch (IllegalArgumentException e) {
if (!registered) {
// Let the test pass
System.err.println("The packet ID " + type + " is not registered.");
assertEquals(e.getMessage(), "The packet ID " + type + " is not registered.");
e.printStackTrace();
} else {
// Something is very wrong
throw e;
}
} catch (RuntimeException e) {
throw new RuntimeException("Failed to serialize packet " + type, e);
} catch (Throwable ex) {
throw new RuntimeException("Failed to serialize packet " + type, ex);
}
}
}
@Test
public void testPacketType() {
assertEquals(PacketType.Legacy.Server.SET_CREATIVE_SLOT, PacketType.findLegacy(107, Sender.SERVER));
}
// Convert to objects that support equals()
private void testEquality(Object a, Object b) {
if (a != null && b != null) {
@@ -0,0 +1,56 @@
/**
* (c) 2016 dmulloy2
*/
package com.comphenix.protocol.injector;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.wrappers.EnumWrappers.ChatType;
import io.netty.buffer.ByteBuf;
/**
* @author dmulloy2
*/
public class WirePacketTest {
@BeforeClass
public static void beforeClass() {
BukkitInitialization.initializePackage();
}
@Test
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);
}
@Test
public void testSerialization() {
int id = 42;
byte[] array = { 1, 3, 7, 21, 88, 67, 8 };
WirePacket packet = new WirePacket(id, array);
ByteBuf buf = packet.serialize();
int backId = WirePacket.readVarInt(buf);
byte[] backArray = new byte[buf.readableBytes()];
buf.readBytes(backArray);
assertEquals(id, backId);
assertArrayEquals(array, backArray);
}
}
@@ -1,14 +1,20 @@
package com.comphenix.protocol.reflect.cloning;
import static org.junit.Assert.assertArrayEquals;
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 org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
public class AggregateClonerTest {
@@ -22,4 +28,20 @@ public class AggregateClonerTest {
List<Integer> input = Arrays.asList(1, 2, 3);
assertEquals(input, AggregateCloner.DEFAULT.clone(input));
}
@Test
public void testNonNullList() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WINDOW_ITEMS);
NonNullList<ItemStack> list = NonNullList.a(16, ItemStack.a);
packet.getModifier().write(1, list);
PacketContainer cloned = packet.deepClone();
@SuppressWarnings("unchecked")
NonNullList<ItemStack> list1 = (NonNullList<ItemStack>) cloned.getModifier().read(1);
assertEquals(list.size(), list1.size());
assertArrayEquals(list.toArray(), list1.toArray());
}
}
@@ -23,21 +23,21 @@ public class UpdaterTest {
private static final int BUKKIT_DEV_ID = 45564;
private static Plugin plugin;
//@BeforeClass
// @BeforeClass
public static void preparePlugin() {
Server server = mock(Server.class);
when(server.getUpdateFolder()).thenReturn(null);
plugin = mock(Plugin.class);
String version = System.getProperty("projectVersion");
if (version == null) version = "4.0.1-SNAPSHOT-b281";
if (version == null) version = "4.2.1";
when(plugin.getDescription()).thenReturn(new PluginDescriptionFile("ProtocolLib", version, null));
when(plugin.getLogger()).thenReturn(Logger.getLogger("ProtocolLib"));
when(plugin.getDataFolder()).thenReturn(null);
when(plugin.getServer()).thenReturn(server);
}
//@Test
// @Test
public void testSpigotUpdater() {
SpigotUpdater updater = new SpigotUpdater(plugin, UpdateType.NO_DOWNLOAD, true);
@@ -54,7 +54,7 @@ public class UpdaterTest {
System.out.println("Update available: " + updater.versionCheck(remote));
}
//@Test
// @Test
public void testBukkitUpdater() {
BukkitUpdater updater = new BukkitUpdater(plugin, BUKKIT_DEV_ID, null, UpdateType.NO_DOWNLOAD, true);
if (! updater.read()) {
@@ -5,6 +5,19 @@ 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;
@@ -18,19 +31,6 @@ import org.powermock.core.classloader.annotations.PowerMockIgnore;
import com.comphenix.protocol.BukkitInitialization;
import com.mojang.authlib.GameProfile;
import net.minecraft.server.v1_9_R2.ChatComponentText;
import net.minecraft.server.v1_9_R2.ChunkCoordIntPair;
import net.minecraft.server.v1_9_R2.DataWatcher;
import net.minecraft.server.v1_9_R2.IBlockData;
import net.minecraft.server.v1_9_R2.IChatBaseComponent;
import net.minecraft.server.v1_9_R2.IChatBaseComponent.ChatSerializer;
import net.minecraft.server.v1_9_R2.NBTCompressedStreamTools;
import net.minecraft.server.v1_9_R2.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.server.v1_9_R2.PlayerConnection;
import net.minecraft.server.v1_9_R2.ServerPing;
import net.minecraft.server.v1_9_R2.ServerPing.ServerData;
import net.minecraft.server.v1_9_R2.ServerPing.ServerPingPlayerSample;
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
public class MinecraftReflectionTest {
@@ -8,7 +8,7 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import net.minecraft.server.v1_9_R2.IntHashMap;
import net.minecraft.server.v1_12_R1.IntHashMap;
import org.bukkit.ChatColor;
import org.bukkit.DyeColor;
@@ -16,15 +16,15 @@ public class ChunkCoordIntPairTest {
@Test
public void test() {
net.minecraft.server.v1_9_R2.ChunkCoordIntPair pair = new net.minecraft.server.v1_9_R2.ChunkCoordIntPair(1, 2);
net.minecraft.server.v1_12_R1.ChunkCoordIntPair pair = new net.minecraft.server.v1_12_R1.ChunkCoordIntPair(1, 2);
ChunkCoordIntPair specific = ChunkCoordIntPair.getConverter().getSpecific(pair);
assertEquals(1, specific.getChunkX());
assertEquals(2, specific.getChunkZ());
net.minecraft.server.v1_9_R2.ChunkCoordIntPair roundtrip =
(net.minecraft.server.v1_9_R2.ChunkCoordIntPair) ChunkCoordIntPair.getConverter().
getGeneric(net.minecraft.server.v1_9_R2.ChunkCoordIntPair.class, specific);
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);
assertEquals(1, roundtrip.x);
assertEquals(2, roundtrip.z);
@@ -1,12 +1,13 @@
package com.comphenix.protocol.wrappers;
import static org.junit.Assert.assertEquals;
import net.minecraft.server.v1_9_R2.EntityHuman.EnumChatVisibility;
import net.minecraft.server.v1_9_R2.EnumDifficulty;
import net.minecraft.server.v1_9_R2.EnumProtocol;
import net.minecraft.server.v1_9_R2.PacketPlayInClientCommand.EnumClientCommand;
import net.minecraft.server.v1_9_R2.PacketPlayInUseEntity.EnumEntityUseAction;
import net.minecraft.server.v1_9_R2.WorldSettings.EnumGamemode;
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 org.junit.BeforeClass;
import org.junit.Test;
@@ -6,9 +6,9 @@ import static org.junit.Assert.assertTrue;
import java.util.List;
import net.minecraft.server.v1_9_R2.AttributeModifier;
import net.minecraft.server.v1_9_R2.PacketPlayOutUpdateAttributes;
import net.minecraft.server.v1_9_R2.PacketPlayOutUpdateAttributes.AttributeSnapshot;
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 org.junit.Before;
import org.junit.BeforeClass;
@@ -19,15 +19,13 @@ 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.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.UUID;
import net.minecraft.server.v1_9_R2.EntityLightning;
import net.minecraft.server.v1_9_R2.ItemStack;
import net.minecraft.server.v1_12_R1.EntityLightning;
import org.bukkit.craftbukkit.v1_9_R2.entity.CraftLightningStrike;
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftLightningStrike;
import org.bukkit.entity.Entity;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -92,11 +90,11 @@ public class WrappedDataWatcherTest {
@Test
public void testSerializers() {
Serializer blockPos = Registry.get(net.minecraft.server.v1_9_R2.BlockPosition.class, false);
Serializer optionalBlockPos = Registry.get(net.minecraft.server.v1_9_R2.BlockPosition.class, true);
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);
assertNotSame(blockPos, optionalBlockPos);
assertNull(Registry.get(ItemStack.class, false));
// assertNull(Registry.get(ItemStack.class, false));
assertNotNull(Registry.get(UUID.class, true));
}
}
@@ -26,8 +26,8 @@ import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import net.minecraft.server.v1_9_R2.ItemStack;
import net.minecraft.server.v1_9_R2.Items;
import net.minecraft.server.v1_12_R1.ItemStack;
import net.minecraft.server.v1_12_R1.Items;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -0,0 +1,41 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 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.wrappers.nbt;
import org.bukkit.block.BlockState;
import org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.BukkitInitialization;
/**
* @author dmulloy2
*/
public class TileEntityTest {
@BeforeClass
public static void beforeClass() {
BukkitInitialization.initializePackage();
}
@Test
public void test() {
// Ensure the read and write methods exist
TileEntityAccessor<BlockState> accessor = new TileEntityAccessor<>();
accessor.findMethods(null, null);
}
}
+4 -18
View File
@@ -14,8 +14,8 @@
<parent>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>v3-SNAPSHOT</version>
<relativePath>../</relativePath>
<version>v4-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<repositories>
@@ -42,8 +42,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
@@ -77,20 +77,6 @@
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>release-sign-artifacts</id>
@@ -178,7 +178,7 @@ public final class Reflection {
}
/**
* Search for the first publically and privately defined method of the given name and parameter count.
* Search for the first publicly and privately defined method of the given name and parameter count.
*
* @param className - lookup name of the class, see {@link #getClass(String)}.
* @param methodName - the method name, or NULL to skip.
@@ -191,7 +191,7 @@ public final class Reflection {
}
/**
* Search for the first publically and privately defined method of the given name and parameter count.
* Search for the first publicly and privately defined method of the given name and parameter count.
*
* @param clazz - a class to start with.
* @param methodName - the method name, or NULL to skip.
@@ -204,7 +204,7 @@ public final class Reflection {
}
/**
* Search for the first publically and privately defined method of the given name and parameter count.
* Search for the first publicly and privately defined method of the given name and parameter count.
*
* @param clazz - a class to start with.
* @param methodName - the method name, or NULL to skip.
@@ -105,8 +105,7 @@ public abstract class TinyProtocol {
} catch (IllegalArgumentException ex) {
// Damn you, late bind
plugin.getLogger().info("[TinyProtocol] Delaying server channel injection due to late bind.");
// Damn you, late bind
new BukkitRunnable() {
@Override
public void run() {
@@ -129,7 +128,7 @@ public abstract class TinyProtocol {
synchronized (networkManagers) {
// Stop injecting channels
if (!closed) {
injectChannelInternal(channel);
channel.eventLoop().submit(() -> injectChannelInternal(channel));
}
}
} catch (Exception e) {
@@ -256,13 +255,12 @@ public abstract class TinyProtocol {
* <p>
* Note that this is not executed on the main thread.
*
* @param reciever - the receiving player, NULL for early login/status packets.
* @param receiver - the receiving player, NULL for early login/status packets.
* @param channel - the channel that received the packet. Never NULL.
* @param remoteAddress - remote address of the sending client. Never NULL.
* @param packet - the packet being sent.
* @return The packet to send instead, or NULL to cancel the transmission.
*/
public Object onPacketOutAsync(Player reciever, Channel channel, Object packet) {
public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) {
return packet;
}
@@ -283,7 +281,7 @@ public abstract class TinyProtocol {
/**
* Send a packet to a particular player.
* <p>
* Note that {@link #onPacketOutAsync(Player, Object)} will be invoked with this packet.
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param player - the destination player.
* @param packet - the packet to send.
@@ -295,7 +293,7 @@ public abstract class TinyProtocol {
/**
* Send a packet to a particular client.
* <p>
* Note that {@link #onPacketOutAsync(Player, Object)} will be invoked with this packet.
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param channel - client identified by a channel.
* @param packet - the packet to send.
@@ -307,7 +305,7 @@ public abstract class TinyProtocol {
/**
* Pretend that a given packet has been received from a player.
* <p>
* Note that {@link #onPacketInAsync(Player, Object)} will be invoked with this packet.
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param player - the player that sent the packet.
* @param packet - the packet that will be received by the server.
@@ -319,7 +317,7 @@ public abstract class TinyProtocol {
/**
* Pretend that a given packet has been received from a given client.
* <p>
* Note that {@link #onPacketInAsync(Player, Object)} will be invoked with this packet.
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param channel - client identified by a channel.
* @param packet - the packet that will be received by the server.
@@ -353,7 +351,7 @@ public abstract class TinyProtocol {
/**
* Add a custom channel handler to the given channel.
*
* @param player - the channel to inject.
* @param channel - the channel to inject.
* @return The intercepted channel, or NULL if it has already been injected.
*/
public void injectChannel(Channel channel) {
@@ -363,7 +361,7 @@ public abstract class TinyProtocol {
/**
* Add a custom channel handler to the given channel.
*
* @param player - the channel to inject.
* @param channel - the channel to inject.
* @return The packet interceptor.
*/
private PacketInterceptor injectChannelInternal(Channel channel) {
+36
View File
@@ -8,6 +8,8 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<minorVersion>4.3.0</minorVersion>
<spigotVersion>1.12-R0.1-SNAPSHOT</spigotVersion>
</properties>
<modules>
@@ -41,6 +43,40 @@
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<!-- Compile with the old version of Netty -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${spigotVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>${spigotVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.comphenix.executors</groupId>
<artifactId>BukkitExecutors</artifactId>
<version>1.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<defaultGoal>clean package install</defaultGoal>