Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4713d320f8 | ||
|
|
083c8e1481 | ||
|
|
6ff6976e85 | ||
|
|
7e87c90b8d | ||
|
|
6fdf0f8255 | ||
|
|
7e12f57eda | ||
|
|
776ec56a2d | ||
|
|
8ec31d83db | ||
|
|
1373911ca2 | ||
|
|
cf5d9dfc7d | ||
|
|
9df0dd48e4 | ||
|
|
d112e9b1dc | ||
|
|
206dc30e17 | ||
|
|
4c3a0afd7e | ||
|
|
8d9bf7d91f | ||
|
|
3ef537242f | ||
|
|
976e4b4217 | ||
|
|
e827a3dd1f | ||
|
|
778031c105 | ||
|
|
6c3654ef40 | ||
|
|
5048f6f34b | ||
|
|
c9dd6e2ba7 | ||
|
|
fc76af9d56 | ||
|
|
aed2285bcb | ||
|
|
1d11908af1 | ||
|
|
3ccc56fef9 | ||
|
|
39e0c974ee | ||
|
|
e52c69ca22 | ||
|
|
d7d53bd905 | ||
|
|
fb2d5016e5 | ||
|
|
015099b92e | ||
|
|
7e8356f0bb | ||
|
|
72b8c4ced8 | ||
|
|
2d22df404a | ||
|
|
0f13a32925 | ||
|
|
1ddb7a0014 | ||
|
|
f9376432c4 | ||
|
|
d195fb6990 | ||
|
|
e4be102a77 | ||
|
|
5f14ccdc34 | ||
|
|
7573459a48 | ||
|
|
01b68cf8e5 | ||
|
|
f3b1a07189 | ||
|
|
c70eb27653 | ||
|
|
4483ce0d79 | ||
|
|
1b23e9ec22 | ||
|
|
1f5692a0c7 | ||
|
|
0ac68dc179 | ||
|
|
d891a057c0 | ||
|
|
9e5bdf4124 | ||
|
|
e1ea295600 | ||
|
|
59171c5443 | ||
|
|
501244cb3c | ||
|
|
390c21f6d5 | ||
|
|
20d78832b0 | ||
|
|
adb3c5392c | ||
|
|
cd2f56d7c7 | ||
|
|
7c9bbc3f6c | ||
|
|
52e396b25d | ||
|
|
aeb2cfb65e | ||
|
|
7f5ea6e493 | ||
|
|
1be94aad78 | ||
|
|
6bb169ac01 | ||
|
|
ea7900d64f | ||
|
|
123abdff30 | ||
|
|
11247f1c01 | ||
|
|
f093c9196d | ||
|
|
81daa62071 | ||
|
|
f99b977888 | ||
|
|
b00ae2ff15 | ||
|
|
b4f9c501b3 | ||
|
|
1738c6f243 | ||
|
|
abea7441a4 | ||
|
|
3887ba27d2 | ||
|
|
76a3af84a1 | ||
|
|
be5d42ee19 | ||
|
|
790711c16e |
No files matched your search
@@ -4,7 +4,8 @@ Make sure you've done the following:
|
||||
- [ ] You're using the latest build for your server version
|
||||
- [ ] This isn't an issue caused by another plugin
|
||||
- [ ] You've checked for duplicate issues
|
||||
- [ ] You didn't use `/reload`
|
||||
|
||||
Debug paste link:
|
||||
|
||||
Description and relevant errors:
|
||||
Description and relevant errors:
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
.settings/
|
||||
bin/
|
||||
.idea/
|
||||
.iml
|
||||
*.iml
|
||||
.ipr
|
||||
.iws
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
- oraclejdk7
|
||||
script: mvn clean test
|
||||
#before_install: cd ProtocolLib
|
||||
install: true
|
||||
|
||||
@@ -44,7 +44,7 @@ You can also add ProtocolLib as a Maven dependency:
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>http://repo.dmulloy2.net/content/groups/public/</url>
|
||||
<url>http://repo.dmulloy2.net/nexus/repository/public/</url>
|
||||
</repository>
|
||||
...
|
||||
</repositories>
|
||||
@@ -52,12 +52,24 @@ You can also add ProtocolLib as a Maven dependency:
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>3.6.5</version>
|
||||
<artifactId>ProtocolLib-API</artifactId>
|
||||
<version>4.4.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
````
|
||||
|
||||
Or use the maven dependency with gradle:
|
||||
|
||||
```gradle
|
||||
repositories {
|
||||
maven { url "http://repo.dmulloy2.net/nexus/repository/public/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.4.0";
|
||||
}
|
||||
```
|
||||
|
||||
Then get a reference to ProtocolManager in onLoad() or onEnable() and you're good to go.
|
||||
|
||||
````java
|
||||
@@ -155,9 +167,3 @@ It's likely that I won't have to update ProtocolLib for anything but bug fixes a
|
||||
How is this possible? It all comes down to reflection in the end. Essentially, no name is hard coded -
|
||||
every field, method and class is deduced by looking at field types, package names or parameter
|
||||
types. It's remarkably consistent across different versions.
|
||||
|
||||
|
||||
### Incompatibility
|
||||
|
||||
The following plugins (to be expanded) are not compatible with ProtocolLib:
|
||||
* TagAPI
|
||||
+7
-66
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>ProtocolLib-API</artifactId>
|
||||
<name>ProtocolLib-API</name>
|
||||
<version>4.2.1</version>
|
||||
<version>4.4.0</version>
|
||||
|
||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
|
||||
@@ -12,7 +12,7 @@
|
||||
<parent>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib-Parent</artifactId>
|
||||
<version>v4-SNAPSHOT</version>
|
||||
<version>4.4.0-SNAPSHOT</version>
|
||||
<relativePath>../../</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
<build>
|
||||
<defaultGoal>clean install</defaultGoal>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
@@ -39,17 +38,17 @@
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
@@ -66,7 +65,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.10</version>
|
||||
<version>2.22.0</version>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<property>
|
||||
@@ -191,62 +190,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.11.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.11.2-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,6 +1,10 @@
|
||||
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;
|
||||
@@ -16,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;
|
||||
@@ -37,7 +40,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Represents an unknown legacy packet ID.
|
||||
* Represents an unknown packet ID.
|
||||
*/
|
||||
public static final int UNKNOWN_PACKET = -1;
|
||||
|
||||
@@ -52,7 +55,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* 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");
|
||||
@@ -60,7 +63,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
private final static Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Client() { super(PacketType.class); }
|
||||
private Client() { super(); }
|
||||
|
||||
public static Client getInstance() {
|
||||
return INSTANCE;
|
||||
@@ -74,10 +77,10 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* 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;
|
||||
@@ -103,85 +106,95 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* 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");
|
||||
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x01, 0x11, "SpawnEntityExperienceOrb");
|
||||
public static final PacketType SPAWN_ENTITY_WEATHER = new PacketType(PROTOCOL, SENDER, 0x02, 0x2C, "SpawnEntityWeather");
|
||||
public static final PacketType SPAWN_ENTITY_LIVING = new PacketType(PROTOCOL, SENDER, 0x03, 0x0F, "SpawnEntityLiving");
|
||||
public static final PacketType SPAWN_ENTITY_PAINTING = new PacketType(PROTOCOL, SENDER, 0x04, 0x10, "SpawnEntityPainting");
|
||||
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x05, 0x0C, "NamedEntitySpawn");
|
||||
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, 0x0B, "Animation");
|
||||
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x07, 0x37, "Statistic");
|
||||
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x08, 0x25, "BlockBreakAnimation");
|
||||
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x09, 0x35, "TileEntityData");
|
||||
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x0A, 0x24, "BlockAction");
|
||||
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0B, 0x23, "BlockChange");
|
||||
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0C, 0xFF, "Boss");
|
||||
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0D, 0x41, "ServerDifficulty");
|
||||
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0E, 0x3A, "TabComplete");
|
||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x0F, 0x02, "Chat");
|
||||
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x10, 0x22, "MultiBlockChange");
|
||||
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x11, 0x32, "Transaction");
|
||||
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x12, 0x2E, "CloseWindow");
|
||||
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x13, 0x2D, "OpenWindow");
|
||||
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x14, 0x30, "WindowItems");
|
||||
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x15, 0x31, "WindowData");
|
||||
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x16, 0x2F, "SetSlot");
|
||||
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x17, 0xFC, "SetCooldown");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x18, 0x3F, "CustomPayload");
|
||||
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x19, 0xFD, "CustomSoundEffect");
|
||||
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1A, 0x40, "KickDisconnect");
|
||||
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1B, 0x1A, "EntityStatus");
|
||||
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1C, 0x27, "Explosion");
|
||||
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1D, 0xFA, "UnloadChunk");
|
||||
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1E, 0x2B, "GameStateChange");
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x1F, 0x00, "KeepAlive");
|
||||
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x20, 0x21, "MapChunk");
|
||||
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x21, 0x28, "WorldEvent");
|
||||
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x22, 0x2A, "WorldParticles");
|
||||
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x23, 0x01, "Login");
|
||||
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x24, 0x34, "Map");
|
||||
public static final PacketType 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 VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x29, 0xFE, "VehicleMove");
|
||||
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2A, 0x36, "OpenSignEditor");
|
||||
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x2B, 0x39, "Abilities");
|
||||
public static final PacketType COMBAT_EVENT = new PacketType(PROTOCOL, SENDER, 0x2C, 0x42, "CombatEvent");
|
||||
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x2D, 0x38, "PlayerInfo");
|
||||
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x2E, 0x08, "Position");
|
||||
public static final PacketType BED = new PacketType(PROTOCOL, SENDER, 0x2F, 0x0A, "Bed");
|
||||
public static final PacketType 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, 0x46, 0x47, "NamedSoundEffect");
|
||||
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x47, 0x48, "PlayerListHeaderFooter");
|
||||
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x48, 0x49, "Collect");
|
||||
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x49, 0x4A, "EntityTeleport");
|
||||
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x4A, 0x4B, "UpdateAttributes");
|
||||
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x4B, 0x4C, "EntityEffect");
|
||||
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "SpawnEntity");
|
||||
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "SpawnEntityExperienceOrb");
|
||||
public static final PacketType SPAWN_ENTITY_WEATHER = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "SpawnEntityWeather");
|
||||
public static final PacketType SPAWN_ENTITY_LIVING = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "SpawnEntityLiving");
|
||||
public static final PacketType SPAWN_ENTITY_PAINTING = new PacketType(PROTOCOL, SENDER, 0x04, 0x04, "SpawnEntityPainting");
|
||||
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x05, 0x05, "NamedEntitySpawn");
|
||||
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, 0x06, "Animation");
|
||||
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x07, 0x07, "Statistic");
|
||||
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x08, 0x08, "BlockBreakAnimation");
|
||||
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x09, 0x09, "TileEntityData");
|
||||
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x0A, 0x0A, "BlockAction");
|
||||
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0B, 0x0B, "BlockChange");
|
||||
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0C, 0x0C, "Boss");
|
||||
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0D, 0x0D, "ServerDifficulty");
|
||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x0E, 0x0F, "Chat");
|
||||
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0F, 0x10, "MultiBlockChange");
|
||||
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x10, 0x0E, "TabComplete");
|
||||
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x11, -0x01, "Commands");
|
||||
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x12, 0x11, "Transaction");
|
||||
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x13, 0x12, "CloseWindow");
|
||||
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x14, 0x13, "OpenWindow");
|
||||
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x15, 0x14, "WindowItems");
|
||||
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x16, 0x15, "WindowData");
|
||||
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x17, 0x16, "SetSlot");
|
||||
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x18, 0x17, "SetCooldown");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x19, 0x18, "CustomPayload");
|
||||
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x1A, 0x19, "CustomSoundEffect");
|
||||
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1B, 0x1A, "KickDisconnect");
|
||||
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1C, 0x1B, "EntityStatus");
|
||||
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x1D, -0x01, "NBTQuery");
|
||||
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1E, 0x1C, "Explosion");
|
||||
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1F, 0x1D, "UnloadChunk");
|
||||
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x20, 0x1E, "GameStateChange");
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x21, 0x1F, "KeepAlive");
|
||||
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x22, 0x20, "MapChunk");
|
||||
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x23, 0x21, "WorldEvent");
|
||||
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x24, 0x22, "WorldParticles");
|
||||
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x25, 0x23, "Login");
|
||||
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x26, 0x24, "Map");
|
||||
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 0x27, 0x25, "Entity");
|
||||
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x28, 0x26, "RelEntityMove");
|
||||
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x29, 0x27, "RelEntityMoveLook");
|
||||
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x2A, 0x28, "EntityLook");
|
||||
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2B, 0x29, "VehicleMove");
|
||||
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2C, 0x2A, "OpenSignEditor");
|
||||
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x2D, 0x2B, "AutoRecipe");
|
||||
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x2E, 0x2C, "Abilities");
|
||||
public static final PacketType COMBAT_EVENT = new PacketType(PROTOCOL, SENDER, 0x2F, 0x2D, "CombatEvent");
|
||||
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x30, 0x2E, "PlayerInfo");
|
||||
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x31, -0x01, "LookAt");
|
||||
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x32, 0x2F, "Position");
|
||||
public static final PacketType BED = new PacketType(PROTOCOL, SENDER, 0x33, 0x30, "Bed");
|
||||
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x34, 0x31, "Recipes");
|
||||
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x35, 0x32, "EntityDestroy");
|
||||
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x36, 0x33, "RemoveEntityEffect");
|
||||
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x37, 0x34, "ResourcePackSend");
|
||||
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x38, 0x35, "Respawn");
|
||||
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x39, 0x36, "EntityHeadRotation");
|
||||
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x3A, 0x37, "SelectAdvancementTab");
|
||||
public static final PacketType WORLD_BORDER = new PacketType(PROTOCOL, SENDER, 0x3B, 0x38, "WorldBorder");
|
||||
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x3C, 0x39, "Camera");
|
||||
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x3D, 0x3A, "HeldItemSlot");
|
||||
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x3E, 0x3B, "ScoreboardDisplayObjective");
|
||||
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x3F, 0x3C, "EntityMetadata");
|
||||
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x40, 0x3D, "AttachEntity");
|
||||
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x41, 0x3E, "EntityVelocity");
|
||||
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x42, 0x3F, "EntityEquipment");
|
||||
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x43, 0x40, "Experience");
|
||||
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x44, 0x41, "UpdateHealth");
|
||||
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x45, 0x42, "ScoreboardObjective");
|
||||
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x46, 0x43, "Mount");
|
||||
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x47, 0x44, "ScoreboardTeam");
|
||||
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x48, 0x45, "ScoreboardScore");
|
||||
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x49, 0x46, "SpawnPosition");
|
||||
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x4A, 0x47, "UpdateTime");
|
||||
public static final PacketType TITLE = new PacketType(PROTOCOL, SENDER, 0x4B, 0x48, "Title");
|
||||
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x4C, -0x01, "StopSound");
|
||||
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x4D, 0x49, "NamedSoundEffect");
|
||||
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x4E, 0x4A, "PlayerListHeaderFooter");
|
||||
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x4F, 0x4B, "Collect");
|
||||
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x50, 0x4C, "EntityTeleport");
|
||||
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x51, 0x4D, "Advancements");
|
||||
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x52, 0x4E, "UpdateAttributes");
|
||||
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x53, 0x4F, "EntityEffect");
|
||||
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x54, 0x54, "RecipeUpdate");
|
||||
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x55, 0x55, "Tags");
|
||||
|
||||
// ---- Removed in 1.9
|
||||
|
||||
@@ -189,59 +202,59 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* @deprecated Removed in 1.9
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType MAP_CHUNK_BULK = new PacketType(PROTOCOL, SENDER, 255, 255, "MapChunkBulk").deprecatedIn(MinecraftVersion.COMBAT_UPDATE);
|
||||
public static final PacketType MAP_CHUNK_BULK = new PacketType(PROTOCOL, SENDER, 255, 255, "MapChunkBulk");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.9
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 254, 254, "SetCompression").deprecatedIn(MinecraftVersion.COMBAT_UPDATE);
|
||||
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 254, 254, "SetCompression");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.9
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType UPDATE_ENTITY_NBT = new PacketType(PROTOCOL, SENDER, 253, 253, "UpdateEntityNBT").deprecatedIn(MinecraftVersion.COMBAT_UPDATE);
|
||||
public static final PacketType UPDATE_ENTITY_NBT = new PacketType(PROTOCOL, SENDER, 253, 253, "UpdateEntityNBT");
|
||||
|
||||
// ----- Renamed packets
|
||||
|
||||
/**
|
||||
* @deprecated Renamed to {@link WINDOW_DATA}
|
||||
* @deprecated Renamed to {@link #WINDOW_DATA}
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType CRAFT_PROGRESS_BAR = WINDOW_DATA.deprecated();
|
||||
public static final PacketType CRAFT_PROGRESS_BAR = WINDOW_DATA.clone();
|
||||
|
||||
/**
|
||||
* @deprecated Renamed to {@link REL_ENTITY_MOVE_LOOK}
|
||||
* @deprecated Renamed to {@link #REL_ENTITY_MOVE_LOOK}
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType ENTITY_MOVE_LOOK = REL_ENTITY_MOVE_LOOK.deprecated();
|
||||
public static final PacketType ENTITY_MOVE_LOOK = REL_ENTITY_MOVE_LOOK.clone();
|
||||
|
||||
/**
|
||||
* @deprecated Renamed to {@link STATISTIC}
|
||||
* @deprecated Renamed to {@link #STATISTIC}
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType STATISTICS = STATISTIC.deprecated();
|
||||
public static final PacketType STATISTICS = STATISTIC.clone();
|
||||
|
||||
/**
|
||||
* @deprecated Renamed to {@link OPEN_SIGN_EDITOR}
|
||||
* @deprecated Renamed to {@link #OPEN_SIGN_EDITOR}
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType OPEN_SIGN_ENTITY = 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 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.deprecated();
|
||||
TILE_ENTITY_DATA.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;
|
||||
@@ -255,44 +268,57 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* 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 TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "TeleportAccept");
|
||||
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x01, -0x01, "TileNBTQuery");
|
||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "Chat");
|
||||
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "ClientCommand");
|
||||
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x04, 0x04, "Settings");
|
||||
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x05, 0x01, "TabComplete");
|
||||
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x06, 0x05, "Transaction");
|
||||
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x07, 0x06, "EnchantItem");
|
||||
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x08, 0x07, "WindowClick");
|
||||
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x09, 0x08, "CloseWindow");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0A, 0x09, "CustomPayload");
|
||||
public static final PacketType BOOK_EDIT = new PacketType(PROTOCOL, SENDER, 0x0B, -0x01, "BEdit");
|
||||
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0C, -0x01, "EntityNBTQuery");
|
||||
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0D, 0x0A, "UseEntity");
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x0E, 0x0B, "KeepAlive");
|
||||
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 0x0F, 0x0C, "Flying");
|
||||
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x10, 0x0D, "Position");
|
||||
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x11, 0x0E, "PositionLook");
|
||||
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x12, 0x0F, "Look");
|
||||
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x13, 0x10, "VehicleMove");
|
||||
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x14, 0x11, "BoatMove");
|
||||
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x15, -0x01, "PickItem");
|
||||
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x16, 0x12, "AutoRecipe");
|
||||
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x17, 0x13, "Abilities");
|
||||
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x18, 0x14, "BlockDig");
|
||||
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x19, 0x15, "EntityAction");
|
||||
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1A, 0x16, "SteerVehicle");
|
||||
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x1B, 0x17, "RecipeDisplayed");
|
||||
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x1C, -0x01, "ItemName");
|
||||
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x1D, 0x18, "ResourcePackStatus");
|
||||
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x1E, 0x19, "Advancements");
|
||||
public static final PacketType TRADE_SELECT = new PacketType(PROTOCOL, SENDER, 0x1F, -0x01, "TrSel");
|
||||
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x20, -0x01, "Beacon");
|
||||
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x21, 0x1A, "HeldItemSlot");
|
||||
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x22, 0x22, "SetCommandBlock");
|
||||
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x23, 0x23, "SetCommandMinecart");
|
||||
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x24, 0x1B, "SetCreativeSlot");
|
||||
public static final PacketType STRUCTURE_BLOCK = new PacketType(PROTOCOL, SENDER, 0x25, 0x25, "Struct");
|
||||
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x26, 0x1C, "UpdateSign");
|
||||
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x27, 0x1D, "ArmAnimation");
|
||||
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x28, 0x1E, "Spectate");
|
||||
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x29, 0x1F, "UseItem");
|
||||
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x2A, 0x20, "BlockPlace");
|
||||
|
||||
private final static Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Client() { super(PacketType.class); }
|
||||
private Client() { super(); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
@@ -318,22 +344,24 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* 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.deprecated();
|
||||
@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;
|
||||
@@ -347,7 +375,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* 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");
|
||||
@@ -356,7 +384,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
private final static Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Client() { super(PacketType.class); }
|
||||
private Client() { super(); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
@@ -382,18 +410,19 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* 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");
|
||||
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "EncryptionBegin");
|
||||
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "Success");
|
||||
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "SetCompression");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x04, -0x01, "CustomPayload");
|
||||
|
||||
private final static Server INSTANCE = new Server();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Server() { super(PacketType.class); }
|
||||
private Server() { super(); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
@@ -407,16 +436,17 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* 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");
|
||||
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "EncryptionBegin");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, -0x01, "CustomPayload");
|
||||
|
||||
private final static Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Client() { super(PacketType.class); }
|
||||
private Client() { super(); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
@@ -443,7 +473,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* @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);
|
||||
@@ -467,7 +497,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
|
||||
// Prevent accidental construction
|
||||
private Server() {
|
||||
super(PacketType.class);
|
||||
super();
|
||||
}
|
||||
|
||||
public static Sender getSender() {
|
||||
@@ -483,7 +513,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* @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);
|
||||
@@ -493,7 +523,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
private final static Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Client() { super(PacketType.class); }
|
||||
private Client() { super(); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
@@ -576,6 +606,13 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -591,9 +628,11 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
private final MinecraftVersion version;
|
||||
private final String[] classNames;
|
||||
|
||||
private boolean forceAsync;
|
||||
private boolean dynamic;
|
||||
private String name;
|
||||
private boolean deprecated;
|
||||
private boolean forceAsync;
|
||||
|
||||
private boolean dynamic;
|
||||
|
||||
/**
|
||||
* Retrieve the current packet/legacy lookup.
|
||||
@@ -644,7 +683,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* @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);
|
||||
|
||||
@@ -659,7 +700,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* @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);
|
||||
@@ -674,7 +717,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* 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;
|
||||
}
|
||||
@@ -682,18 +727,19 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@@ -715,7 +761,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
}
|
||||
}
|
||||
|
||||
public static String format(Protocol protocol, Sender sender, String name) {
|
||||
private static String format(Protocol protocol, Sender sender, String name) {
|
||||
if (name.contains("Packet"))
|
||||
return name;
|
||||
|
||||
@@ -728,9 +774,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* @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;
|
||||
}
|
||||
@@ -743,7 +787,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* @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);
|
||||
|
||||
@@ -878,7 +924,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
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);
|
||||
@@ -907,7 +953,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* @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();
|
||||
@@ -954,7 +1000,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
for (int i = 0; i < classNames.length; i++) {
|
||||
classNames[i] = format(protocol, sender, names[i]);
|
||||
}
|
||||
//System.out.println(Arrays.toString(classNames));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1012,11 +1057,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* <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;
|
||||
}
|
||||
@@ -1037,12 +1080,47 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1058,7 +1136,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* <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;
|
||||
}
|
||||
@@ -1071,40 +1151,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
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;
|
||||
}
|
||||
|
||||
private PacketType deprecatedIn(MinecraftVersion version) {
|
||||
try {
|
||||
return MinecraftVersion.getCurrentVersion().isAtLeast(version) ? deprecated() : this;
|
||||
} catch (Throwable ex) {
|
||||
return deprecated();
|
||||
}
|
||||
}
|
||||
|
||||
private PacketType deprecated() {
|
||||
PacketType ret = clone();
|
||||
ret.deprecated = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public boolean isDeprecated() {
|
||||
return deprecated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(protocol, sender, currentId, legacyId);
|
||||
return Objects.hashCode(protocol, sender, currentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1116,8 +1165,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
PacketType other = (PacketType) obj;
|
||||
return protocol == other.protocol &&
|
||||
sender == other.sender &&
|
||||
currentId == other.currentId &&
|
||||
legacyId == other.legacyId;
|
||||
currentId == other.currentId;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1128,7 +1176,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
compare(protocol, other.getProtocol()).
|
||||
compare(sender, other.getSender()).
|
||||
compare(currentId, other.getCurrentId()).
|
||||
compare(legacyId, other.getLegacyId()).
|
||||
result();
|
||||
}
|
||||
|
||||
@@ -1137,9 +1184,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
Class<?> clazz = getPacketClass();
|
||||
|
||||
if (clazz == null)
|
||||
return name() + "[" + protocol + ", " + sender + ", " + currentId + ", legacy: " + legacyId + ", classNames: " + Arrays.toString(classNames) + " (unregistered)]";
|
||||
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
|
||||
|
||||
+57
-41
@@ -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,40 +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 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,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;
|
||||
}
|
||||
|
||||
@@ -89,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();
|
||||
}
|
||||
}
|
||||
@@ -39,12 +39,12 @@ public class ProtocolLibrary {
|
||||
/**
|
||||
* The maximum version ProtocolLib has been tested with.
|
||||
*/
|
||||
public static final String MAXIMUM_MINECRAFT_VERSION = "1.11.2";
|
||||
public static final String MAXIMUM_MINECRAFT_VERSION = "1.13.1";
|
||||
|
||||
/**
|
||||
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.11) was released.
|
||||
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.13.1) was released.
|
||||
*/
|
||||
public static final String MINECRAFT_LAST_RELEASE_DATE = "2016-12-21";
|
||||
public static final String MINECRAFT_LAST_RELEASE_DATE = "2018-08-22";
|
||||
|
||||
/**
|
||||
* Plugins that are currently incompatible with ProtocolLib.
|
||||
|
||||
@@ -31,11 +31,11 @@ public class ProtocolLogger {
|
||||
ProtocolLogger.plugin = plugin;
|
||||
}
|
||||
|
||||
private static boolean isDebugEnabled() {
|
||||
public static boolean isDebugEnabled() {
|
||||
try {
|
||||
return plugin.getConfig().getBoolean("global.debug", false);
|
||||
} catch (Throwable ex) { // Maybe we're testing or something
|
||||
return false;
|
||||
} catch (Throwable ex) { // Enable in testing environments
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,37 +17,15 @@
|
||||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.WorldType;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.injector.StructureCache;
|
||||
@@ -55,54 +33,15 @@ import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.ObjectWriter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.cloning.AggregateCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.*;
|
||||
import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters;
|
||||
import com.comphenix.protocol.reflect.cloning.BukkitCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.Cloner;
|
||||
import com.comphenix.protocol.reflect.cloning.CollectionCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.FieldCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.ImmutableDetector;
|
||||
import com.comphenix.protocol.reflect.cloning.OptionalCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.SerializableCloner;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.StreamSerializer;
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.ChunkCoordIntPair;
|
||||
import com.comphenix.protocol.wrappers.ChunkPosition;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.ChatVisibility;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.ClientCommand;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.CombatEventType;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Difficulty;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.EnumConverter;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.ItemSlot;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Particle;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerAction;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerDigType;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerInfoAction;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.ResourcePackStatus;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.ScoreboardAction;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.TitleAction;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.WorldBorderAction;
|
||||
import com.comphenix.protocol.wrappers.MultiBlockChangeInfo;
|
||||
import com.comphenix.protocol.wrappers.PlayerInfoData;
|
||||
import com.comphenix.protocol.wrappers.WrappedAttribute;
|
||||
import com.comphenix.protocol.wrappers.WrappedBlockData;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedServerPing;
|
||||
import com.comphenix.protocol.wrappers.WrappedStatistic;
|
||||
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
|
||||
import com.comphenix.protocol.wrappers.*;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.*;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtBase;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
@@ -114,6 +53,15 @@ import com.google.common.collect.Sets;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.WorldType;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
/**
|
||||
* Represents a Minecraft packet indirectly.
|
||||
*
|
||||
@@ -133,30 +81,30 @@ public class PacketContainer implements Serializable {
|
||||
private static ConcurrentMap<Class<?>, Method> readMethods = Maps.newConcurrentMap();
|
||||
|
||||
// Used to clone packets
|
||||
private static final AggregateCloner DEEP_CLONER = AggregateCloner.newBuilder().
|
||||
instanceProvider(DefaultInstances.DEFAULT).
|
||||
andThen(BukkitCloner.class).
|
||||
andThen(ImmutableDetector.class).
|
||||
andThen(OptionalCloner.class).
|
||||
andThen(CollectionCloner.class).
|
||||
andThen(getSpecializedDeepClonerFactory()).
|
||||
build();
|
||||
private static final AggregateCloner DEEP_CLONER = AggregateCloner
|
||||
.newBuilder()
|
||||
.instanceProvider(DefaultInstances.DEFAULT)
|
||||
.andThen(BukkitCloner.class)
|
||||
.andThen(ImmutableDetector.class)
|
||||
.andThen(JavaOptionalCloner.class)
|
||||
.andThen(GuavaOptionalCloner.class)
|
||||
.andThen(CollectionCloner.class)
|
||||
.andThen(getSpecializedDeepClonerFactory())
|
||||
.build();
|
||||
|
||||
private static final AggregateCloner SHALLOW_CLONER = AggregateCloner.newBuilder().
|
||||
instanceProvider(DefaultInstances.DEFAULT).
|
||||
andThen(new Function<BuilderParameters, Cloner>() {
|
||||
@Override
|
||||
public Cloner apply(@Nullable BuilderParameters param) {
|
||||
if (param == null)
|
||||
throw new IllegalArgumentException("Cannot be NULL.");
|
||||
|
||||
return new FieldCloner(param.getAggregateCloner(), param.getInstanceProvider()) {{
|
||||
// Use a default writer with no concept of cloning
|
||||
writer = new ObjectWriter();
|
||||
}};
|
||||
}
|
||||
}).
|
||||
build();
|
||||
private static final AggregateCloner SHALLOW_CLONER = AggregateCloner
|
||||
.newBuilder()
|
||||
.instanceProvider(DefaultInstances.DEFAULT)
|
||||
.andThen(param -> {
|
||||
if (param == null)
|
||||
throw new IllegalArgumentException("Cannot be NULL.");
|
||||
|
||||
return new FieldCloner(param.getAggregateCloner(), param.getInstanceProvider()) {{
|
||||
// Use a default writer with no concept of cloning
|
||||
writer = new ObjectWriter();
|
||||
}};
|
||||
})
|
||||
.build();
|
||||
|
||||
// Packets that cannot be cloned by our default deep cloner
|
||||
private static final Set<PacketType> CLONING_UNSUPPORTED = Sets.newHashSet(
|
||||
@@ -384,8 +332,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<ItemStack> getItemModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<ItemStack>withType(
|
||||
MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter());
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getItemStackClass(),
|
||||
BukkitConverters.getItemStackConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,9 +346,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<ItemStack[]> getItemArrayModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<ItemStack[]>withType(
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getItemStackArrayClass(),
|
||||
BukkitConverters.getIgnoreNull(new ItemStackArrayConverter()));
|
||||
Converters.ignoreNull(new ItemStackArrayConverter()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,10 +362,7 @@ public class PacketContainer implements Serializable {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.withType(
|
||||
List.class,
|
||||
BukkitConverters.getListConverter(
|
||||
MinecraftReflection.getItemStackClass(),
|
||||
BukkitConverters.getItemStackConverter()
|
||||
)
|
||||
BukkitConverters.getListConverter(BukkitConverters.getItemStackConverter())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -427,12 +373,9 @@ public class PacketContainer implements Serializable {
|
||||
* @return A modifier for maps of statistics.
|
||||
*/
|
||||
public StructureModifier<Map<WrappedStatistic, Integer>> getStatisticMaps() {
|
||||
return structureModifier.withType(Map.class,
|
||||
BukkitConverters.<WrappedStatistic, Integer>getMapConverter(
|
||||
MinecraftReflection.getStatisticClass(),
|
||||
BukkitConverters.getWrappedStatisticConverter()
|
||||
)
|
||||
);
|
||||
return getMaps(
|
||||
BukkitConverters.getWrappedStatisticConverter(),
|
||||
Converters.passthrough(Integer.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -444,7 +387,7 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<WorldType> getWorldTypeModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<WorldType>withType(
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getWorldTypeClass(),
|
||||
BukkitConverters.getWorldTypeConverter());
|
||||
}
|
||||
@@ -455,7 +398,7 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<WrappedDataWatcher> getDataWatcherModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<WrappedDataWatcher>withType(
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getDataWatcherClass(),
|
||||
BukkitConverters.getDataWatcherConverter());
|
||||
}
|
||||
@@ -474,7 +417,7 @@ public class PacketContainer implements Serializable {
|
||||
public StructureModifier<Entity> getEntityModifier(@Nonnull World world) {
|
||||
Preconditions.checkNotNull(world, "world cannot be NULL.");
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<Entity>withType(
|
||||
return structureModifier.withType(
|
||||
int.class, BukkitConverters.getEntityConverter(world));
|
||||
}
|
||||
|
||||
@@ -546,10 +489,7 @@ public class PacketContainer implements Serializable {
|
||||
// Convert to and from the ProtocolLib wrapper
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(
|
||||
MinecraftReflection.getNBTBaseClass(),
|
||||
BukkitConverters.getNbtConverter()
|
||||
)
|
||||
BukkitConverters.getListConverter(BukkitConverters.getNbtConverter())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -575,9 +515,7 @@ public class PacketContainer implements Serializable {
|
||||
// Convert to and from the ProtocolLib wrapper
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(
|
||||
MinecraftReflection.getAttributeSnapshotClass(),
|
||||
BukkitConverters.getWrappedAttributeConverter())
|
||||
BukkitConverters.getListConverter(BukkitConverters.getWrappedAttributeConverter())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -593,10 +531,7 @@ public class PacketContainer implements Serializable {
|
||||
// Convert to and from the ProtocolLib wrapper
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(
|
||||
MinecraftReflection.getChunkPositionClass(),
|
||||
ChunkPosition.getConverter())
|
||||
);
|
||||
BukkitConverters.getListConverter(ChunkPosition.getConverter()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -611,10 +546,7 @@ public class PacketContainer implements Serializable {
|
||||
// Convert to and from the ProtocolLib wrapper
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(
|
||||
MinecraftReflection.getBlockPositionClass(),
|
||||
BlockPosition.getConverter())
|
||||
);
|
||||
BukkitConverters.getListConverter(BlockPosition.getConverter()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -628,10 +560,7 @@ public class PacketContainer implements Serializable {
|
||||
// Convert to and from the ProtocolLib wrapper
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(
|
||||
MinecraftReflection.getDataWatcherItemClass(),
|
||||
BukkitConverters.getWatchableObjectConverter())
|
||||
);
|
||||
BukkitConverters.getListConverter(BukkitConverters.getWatchableObjectConverter()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -643,7 +572,7 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<Material> getBlocks() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<Material>withType(
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getBlockClass(), BukkitConverters.getBlockConverter());
|
||||
}
|
||||
|
||||
@@ -656,7 +585,7 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<WrappedGameProfile> getGameProfiles() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<WrappedGameProfile>withType(
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getGameProfileClass(), BukkitConverters.getWrappedGameProfileConverter());
|
||||
}
|
||||
|
||||
@@ -669,7 +598,7 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<WrappedBlockData> getBlockData() {
|
||||
// Convert to and from our wrapper
|
||||
return structureModifier.<WrappedBlockData>withType(
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter());
|
||||
}
|
||||
|
||||
@@ -684,7 +613,7 @@ public class PacketContainer implements Serializable {
|
||||
ChunkCoordIntPair chunk = getChunkCoordIntPairs().read(0);
|
||||
|
||||
// Convert to and from our wrapper
|
||||
return structureModifier.<MultiBlockChangeInfo[]>withType(
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getMultiBlockChangeInfoArrayClass(), MultiBlockChangeInfo.getArrayConverter(chunk));
|
||||
}
|
||||
|
||||
@@ -697,7 +626,7 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<WrappedChatComponent> getChatComponents() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<WrappedChatComponent>withType(
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
|
||||
}
|
||||
|
||||
@@ -714,22 +643,23 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<WrappedChatComponent[]> getChatComponentArrays() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<WrappedChatComponent[]>withType(
|
||||
return structureModifier.withType(
|
||||
ComponentArrayConverter.getGenericType(),
|
||||
BukkitConverters.getIgnoreNull(new ComponentArrayConverter()));
|
||||
Converters.ignoreNull(new ComponentArrayConverter()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the ServerPing fields in the following packet: <br>
|
||||
* <ul>
|
||||
* <li>{@link PacketType.Status.Server#OUT_SERVER_INFO}
|
||||
* <li>{@link PacketType.Status.Server#SERVER_INFO}
|
||||
* </ul>
|
||||
* @return A modifier for ServerPing fields.
|
||||
*/
|
||||
public StructureModifier<WrappedServerPing> getServerPings() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<WrappedServerPing>withType(
|
||||
MinecraftReflection.getServerPingClass(), BukkitConverters.getWrappedServerPingConverter());
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getServerPingClass(),
|
||||
BukkitConverters.getWrappedServerPingConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -743,10 +673,7 @@ public class PacketContainer implements Serializable {
|
||||
// Convert to and from the ProtocolLib wrapper
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(
|
||||
MinecraftReflection.getPlayerInfoDataClass(),
|
||||
PlayerInfoData.getConverter())
|
||||
);
|
||||
BukkitConverters.getListConverter(PlayerInfoData.getConverter()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -755,8 +682,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<Protocol> getProtocols() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<Protocol>withType(
|
||||
EnumWrappers.getProtocolClass(), EnumWrappers.getProtocolConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getProtocolClass(),
|
||||
EnumWrappers.getProtocolConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -765,8 +693,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<ClientCommand> getClientCommands() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<ClientCommand>withType(
|
||||
EnumWrappers.getClientCommandClass(), EnumWrappers.getClientCommandConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getClientCommandClass(),
|
||||
EnumWrappers.getClientCommandConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -775,8 +704,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<ChatVisibility> getChatVisibilities() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<ChatVisibility>withType(
|
||||
EnumWrappers.getChatVisibilityClass(), EnumWrappers.getChatVisibilityConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getChatVisibilityClass(),
|
||||
EnumWrappers.getChatVisibilityConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -785,8 +715,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<Difficulty> getDifficulties() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<Difficulty>withType(
|
||||
EnumWrappers.getDifficultyClass(), EnumWrappers.getDifficultyConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getDifficultyClass(),
|
||||
EnumWrappers.getDifficultyConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -795,8 +726,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<EntityUseAction> getEntityUseActions() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<EntityUseAction>withType(
|
||||
EnumWrappers.getEntityUseActionClass(), EnumWrappers.getEntityUseActionConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getEntityUseActionClass(),
|
||||
EnumWrappers.getEntityUseActionConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -805,8 +737,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<NativeGameMode> getGameModes() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<NativeGameMode>withType(
|
||||
EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getGameModeClass(),
|
||||
EnumWrappers.getGameModeConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -815,8 +748,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<ResourcePackStatus> getResourcePackStatus() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<ResourcePackStatus>withType(
|
||||
EnumWrappers.getResourcePackStatusClass(), EnumWrappers.getResourcePackStatusConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getResourcePackStatusClass(),
|
||||
EnumWrappers.getResourcePackStatusConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -825,8 +759,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<PlayerInfoAction> getPlayerInfoAction() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<PlayerInfoAction>withType(
|
||||
EnumWrappers.getPlayerInfoActionClass(), EnumWrappers.getPlayerInfoActionConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getPlayerInfoActionClass(),
|
||||
EnumWrappers.getPlayerInfoActionConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -835,8 +770,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<TitleAction> getTitleActions() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<TitleAction>withType(
|
||||
EnumWrappers.getTitleActionClass(), EnumWrappers.getTitleActionConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getTitleActionClass(),
|
||||
EnumWrappers.getTitleActionConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -845,8 +781,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<WorldBorderAction> getWorldBorderActions() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<WorldBorderAction>withType(
|
||||
EnumWrappers.getWorldBorderActionClass(), EnumWrappers.getWorldBorderActionConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getWorldBorderActionClass(),
|
||||
EnumWrappers.getWorldBorderActionConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -855,8 +792,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<CombatEventType> getCombatEvents() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<CombatEventType>withType(
|
||||
EnumWrappers.getCombatEventTypeClass(), EnumWrappers.getCombatEventTypeConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getCombatEventTypeClass(),
|
||||
EnumWrappers.getCombatEventTypeConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -865,8 +803,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<PlayerDigType> getPlayerDigTypes() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<PlayerDigType>withType(
|
||||
EnumWrappers.getPlayerDigTypeClass(), EnumWrappers.getPlayerDiggingActionConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getPlayerDigTypeClass(),
|
||||
EnumWrappers.getPlayerDiggingActionConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -875,8 +814,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<PlayerAction> getPlayerActions() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<PlayerAction>withType(
|
||||
EnumWrappers.getPlayerActionClass(), EnumWrappers.getEntityActionConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getPlayerActionClass(),
|
||||
EnumWrappers.getEntityActionConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -885,18 +825,32 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<ScoreboardAction> getScoreboardActions() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<ScoreboardAction>withType(
|
||||
EnumWrappers.getScoreboardActionClass(), EnumWrappers.getUpdateScoreActionConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getScoreboardActionClass(),
|
||||
EnumWrappers.getUpdateScoreActionConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the Particle enum in 1.8.
|
||||
* Retrieve a read/write structure for the Particle enum in 1.8-1.12.
|
||||
* <b>NOTE:</b> This will produce undesirable results in 1.13
|
||||
* @return A modifier for Particle enum fields.
|
||||
*/
|
||||
public StructureModifier<Particle> getParticles() {
|
||||
public StructureModifier<EnumWrappers.Particle> getParticles() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<Particle>withType(
|
||||
EnumWrappers.getParticleClass(), EnumWrappers.getParticleConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getParticleClass(),
|
||||
EnumWrappers.getParticleConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for ParticleParams in 1.13
|
||||
* @return A modifier for ParticleParam fields.
|
||||
*/
|
||||
public StructureModifier<WrappedParticle> getNewParticles() {
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getMinecraftClass("ParticleParam"),
|
||||
BukkitConverters.getParticleConverter()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -905,8 +859,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<PotionEffectType> getEffectTypes() {
|
||||
// Convert to and from Bukkit
|
||||
return structureModifier.<PotionEffectType>withType(
|
||||
MinecraftReflection.getMobEffectListClass(), BukkitConverters.getEffectTypeConverter());
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getMobEffectListClass(),
|
||||
BukkitConverters.getEffectTypeConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -915,8 +870,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<SoundCategory> getSoundCategories() {
|
||||
// Convert to and from the enums
|
||||
return structureModifier.<SoundCategory>withType(
|
||||
EnumWrappers.getSoundCategoryClass(), EnumWrappers.getSoundCategoryConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getSoundCategoryClass(),
|
||||
EnumWrappers.getSoundCategoryConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -925,8 +881,9 @@ public class PacketContainer implements Serializable {
|
||||
*/
|
||||
public StructureModifier<Sound> getSoundEffects() {
|
||||
// Convert to and from Bukkit
|
||||
return structureModifier.<Sound>withType(
|
||||
MinecraftReflection.getSoundEffectClass(), BukkitConverters.getSoundConverter());
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getSoundEffectClass(),
|
||||
BukkitConverters.getSoundConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -934,8 +891,9 @@ public class PacketContainer implements Serializable {
|
||||
* @return A modifier for ItemSlot enum fields.
|
||||
*/
|
||||
public StructureModifier<ItemSlot> getItemSlots() {
|
||||
return structureModifier.<ItemSlot>withType(
|
||||
EnumWrappers.getItemSlotClass(), EnumWrappers.getItemSlotConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getItemSlotClass(),
|
||||
EnumWrappers.getItemSlotConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -943,8 +901,9 @@ public class PacketContainer implements Serializable {
|
||||
* @return A modifier for Hand enum fields.
|
||||
*/
|
||||
public StructureModifier<Hand> getHands() {
|
||||
return structureModifier.<Hand>withType(
|
||||
EnumWrappers.getHandClass(), EnumWrappers.getHandConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getHandClass(),
|
||||
EnumWrappers.getHandConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -952,8 +911,85 @@ public class PacketContainer implements Serializable {
|
||||
* @return A modifier for Direction enum fields.
|
||||
*/
|
||||
public StructureModifier<Direction> getDirections() {
|
||||
return structureModifier.<Direction>withType(
|
||||
EnumWrappers.getDirectionClass(), EnumWrappers.getDirectionConverter());
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getDirectionClass(),
|
||||
EnumWrappers.getDirectionConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.withType(
|
||||
EnumWrappers.getChatTypeClass(),
|
||||
EnumWrappers.getChatTypeConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the MinecraftKey class.
|
||||
* @return A modifier for MinecraftKey fields.
|
||||
*/
|
||||
public StructureModifier<MinecraftKey> getMinecraftKeys() {
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getMinecraftKeyClass(),
|
||||
MinecraftKey.getConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive a read/write structure for dimension IDs in 1.13.1+
|
||||
* @return A modifier for dimension IDs
|
||||
*/
|
||||
public StructureModifier<Integer> getDimensions() {
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getMinecraftClass("DimensionManager"),
|
||||
BukkitConverters.getDimensionIDConverter()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the Map class.
|
||||
* @param keyConverter Converter for map keys
|
||||
* @param valConverter Converter for map values
|
||||
* @param <K> Key param
|
||||
* @param <V> Value param
|
||||
* @return A modifier for Map fields.
|
||||
*
|
||||
* @see BukkitConverters
|
||||
* @see EquivalentConverter
|
||||
*/
|
||||
public <K, V> StructureModifier<Map<K, V>> getMaps(EquivalentConverter<K> keyConverter,
|
||||
EquivalentConverter<V> valConverter) {
|
||||
return structureModifier.withType(
|
||||
Map.class,
|
||||
BukkitConverters.getMapConverter(keyConverter, valConverter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the Set class.
|
||||
* @param converter Converter for elements
|
||||
* @param <E> Element param
|
||||
* @return A modifier for Set fields
|
||||
*
|
||||
* @see BukkitConverters
|
||||
* @see EquivalentConverter
|
||||
*/
|
||||
public <E> StructureModifier<Set<E>> getSets(EquivalentConverter<E> converter) {
|
||||
return structureModifier.withType(
|
||||
Set.class,
|
||||
BukkitConverters.getSetConverter(converter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the List class.
|
||||
* @param converter Converter for elements
|
||||
* @param <E> Element param
|
||||
* @return A modifier for List fields
|
||||
*/
|
||||
public <E> StructureModifier<List<E>> getLists(EquivalentConverter<E> converter) {
|
||||
return structureModifier.withType(
|
||||
List.class,
|
||||
BukkitConverters.getListConverter(converter));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -967,7 +1003,9 @@ public class PacketContainer implements Serializable {
|
||||
* @return The modifier
|
||||
*/
|
||||
public <T extends Enum<T>> StructureModifier<T> getEnumModifier(Class<T> enumClass, Class<?> nmsClass) {
|
||||
return structureModifier.<T>withType(nmsClass, new EnumConverter<T>(enumClass));
|
||||
return structureModifier.withType(
|
||||
nmsClass,
|
||||
new EnumConverter<>(nmsClass, enumClass));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -980,7 +1018,9 @@ public class PacketContainer implements Serializable {
|
||||
* @see #getEnumModifier(Class, Class)
|
||||
*/
|
||||
public <T extends Enum<T>> StructureModifier<T> getEnumModifier(Class<T> enumClass, int index) {
|
||||
return getEnumModifier(enumClass, structureModifier.getField(index).getType());
|
||||
return getEnumModifier(
|
||||
enumClass,
|
||||
structureModifier.getField(index).getType());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1026,7 +1066,7 @@ public class PacketContainer implements Serializable {
|
||||
* @return A deep copy of the current packet.
|
||||
*/
|
||||
public PacketContainer deepClone() {
|
||||
Object clonedPacket = null;
|
||||
Object clonedPacket;
|
||||
|
||||
// Fall back on the alternative (but slower) method of reading and writing back the packet
|
||||
if (CLONING_UNSUPPORTED.contains(type)) {
|
||||
@@ -1146,21 +1186,52 @@ public class PacketContainer implements Serializable {
|
||||
}
|
||||
|
||||
// ---- Metadata
|
||||
// This map will only be initialized if it is actually used
|
||||
private Map<String, Object> metadata;
|
||||
|
||||
/**
|
||||
* Gets the metadata value for a given key if it exists. Packet metadata expires after a minute, which is far longer
|
||||
* than a packet will ever be held in processing.
|
||||
*
|
||||
* @param key Metadata key
|
||||
* @param <T> Metadata type
|
||||
* @return The metadata value, or an empty optional
|
||||
*/
|
||||
public <T> Optional<T> getMeta(String key) {
|
||||
return PacketMetadata.get(handle, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the metadata value at a given key. Packet metadata expires after a minute, which is far longer than a packet
|
||||
* will ever be held in processing.
|
||||
*
|
||||
* @param key Metadata key
|
||||
* @param value Metadata value
|
||||
* @param <T> Metadata type
|
||||
*/
|
||||
public <T> void setMeta(String key, T value) {
|
||||
PacketMetadata.set(handle, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the metadata for a given key if it exists.
|
||||
* @param key Key to remove meta for
|
||||
*/
|
||||
public void removeMeta(String key) {
|
||||
PacketMetadata.remove(handle, key);
|
||||
}
|
||||
|
||||
// ---- Old Metadata API
|
||||
// Scheduled for removal in 4.5
|
||||
|
||||
/**
|
||||
* Gets the metadata value for a given key.
|
||||
*
|
||||
* @param key Metadata key
|
||||
* @return Metadata value, or null if nonexistent.
|
||||
* @deprecated Replaced with {@link #getMeta(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public Object getMetadata(String key) {
|
||||
if (metadata != null) {
|
||||
return metadata.get(key);
|
||||
}
|
||||
|
||||
return null;
|
||||
return getMeta(key).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1170,13 +1241,11 @@ public class PacketContainer implements Serializable {
|
||||
*
|
||||
* @param key Metadata key
|
||||
* @param value Metadata value
|
||||
* @deprecated Replaced by {@link #setMeta(String, Object)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void addMetadata(String key, Object value) {
|
||||
if (metadata == null) {
|
||||
metadata = new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
metadata.put(key, value);
|
||||
setMeta(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1186,18 +1255,11 @@ public class PacketContainer implements Serializable {
|
||||
*
|
||||
* @param key Metadata key
|
||||
* @return The previous value, or null if nonexistant.
|
||||
* @deprecated Replaced by {@link #removeMeta(String)}. This one was pretty much just for naming consistency.
|
||||
*/
|
||||
@Deprecated
|
||||
public Object removeMetadata(String key) {
|
||||
if (metadata != null) {
|
||||
Object value = metadata.remove(key);
|
||||
if (metadata.isEmpty()) {
|
||||
metadata = null;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
return PacketMetadata.remove(handle, key).orElseGet(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1205,9 +1267,11 @@ public class PacketContainer implements Serializable {
|
||||
*
|
||||
* @param key Metadata key
|
||||
* @return True if this packet has metadata for the key, false if not.
|
||||
* @deprecated Replaced with {@code getMeta(key).isPresent()}
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean hasMetadata(String key) {
|
||||
return metadata != null && metadata.containsKey(key);
|
||||
return getMeta(key).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1249,13 +1313,13 @@ public class PacketContainer implements Serializable {
|
||||
final EquivalentConverter<ItemStack> stackConverter = BukkitConverters.getItemStackConverter();
|
||||
|
||||
@Override
|
||||
public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
|
||||
public Object getGeneric(ItemStack[] specific) {
|
||||
Class<?> nmsStack = MinecraftReflection.getItemStackClass();
|
||||
Object[] result = (Object[]) Array.newInstance(nmsStack, specific.length);
|
||||
|
||||
// Unwrap every item
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = stackConverter.getGeneric(nmsStack, specific[i]);
|
||||
result[i] = stackConverter.getGeneric(specific[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1286,13 +1350,13 @@ public class PacketContainer implements Serializable {
|
||||
final EquivalentConverter<WrappedChatComponent> componentConverter = BukkitConverters.getWrappedChatComponentConverter();
|
||||
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
|
||||
public Object getGeneric(WrappedChatComponent[] specific) {
|
||||
Class<?> nmsComponent = MinecraftReflection.getIChatBaseComponentClass();
|
||||
Object[] result = (Object[]) Array.newInstance(nmsComponent, specific.length);
|
||||
|
||||
// Unwrap every item
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = componentConverter.getGeneric(nmsComponent, specific[i]);
|
||||
result[i] = componentConverter.getGeneric(specific[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1341,7 +1405,7 @@ public class PacketContainer implements Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
|
||||
public Object getGeneric(WrappedChatComponent[] specific) {
|
||||
NbtCompound compound = NbtFactory.ofCompound("");
|
||||
|
||||
for (int i = 0; i < lines; i++) {
|
||||
@@ -1355,7 +1419,7 @@ public class PacketContainer implements Serializable {
|
||||
compound.put("Text" + (i + 1), component.getJson());
|
||||
}
|
||||
|
||||
return nbtConverter.getGeneric(genericType, compound);
|
||||
return nbtConverter.getGeneric(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1371,7 +1435,7 @@ public class PacketContainer implements Serializable {
|
||||
private static class ComponentArrayConverter implements EquivalentConverter<WrappedChatComponent[]> {
|
||||
private static final EquivalentConverter<WrappedChatComponent[]> DELEGATE;
|
||||
static {
|
||||
if (!PacketType.Play.Server.UPDATE_SIGN.isDeprecated()) {
|
||||
if (MinecraftReflection.signUpdateExists()) {
|
||||
DELEGATE = new LegacyComponentConverter();
|
||||
} else {
|
||||
DELEGATE = new NBTComponentConverter();
|
||||
@@ -1384,8 +1448,8 @@ public class PacketContainer implements Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
|
||||
return DELEGATE.getGeneric(genericType, specific);
|
||||
public Object getGeneric(WrappedChatComponent[] specific) {
|
||||
return DELEGATE.getGeneric(specific);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.comphenix.protocol.async.AsyncMarker;
|
||||
import com.comphenix.protocol.error.PluginContext;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayer;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
@@ -321,7 +322,41 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
* @return The player associated with this event.
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return playerReference.get();
|
||||
Player player = playerReference.get();
|
||||
|
||||
// Check if the player has been updated so we can do more stuff
|
||||
if (player instanceof TemporaryPlayer) {
|
||||
Player updated = player.getPlayer();
|
||||
if (updated != null && !(updated instanceof TemporaryPlayer)) {
|
||||
playerReference.clear();
|
||||
playerReference = new WeakReference<>(updated);
|
||||
return updated;
|
||||
}
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the player in this PacketEvent is temporary (i.e. they don't have a true player instance yet).
|
||||
* Temporary players have a limited subset of methods that may be used:
|
||||
* <ul>
|
||||
* <li>getPlayer</li>
|
||||
* <li>getAddress</li>
|
||||
* <li>getServer</li>
|
||||
* <li>chat</li>
|
||||
* <li>sendMessage</li>
|
||||
* <li>kickPlayer</li>
|
||||
* <li>isOnline</li>
|
||||
* </ul>
|
||||
*
|
||||
* Anything else will throw an UnsupportedOperationException. Use this check before calling other methods when
|
||||
* dealing with packets early in the login sequence or if you get the aforementioned exception.
|
||||
*
|
||||
* @return True if the player is temporary, false if not.
|
||||
*/
|
||||
public boolean isPlayerTemporary() {
|
||||
return getPlayer() instanceof TemporaryPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2017 dmulloy2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* Stores and retrieves metadata for applicable packet objects.
|
||||
* @author dmulloy2
|
||||
*/
|
||||
class PacketMetadata {
|
||||
|
||||
private static class MetaObject<T> {
|
||||
private final String key;
|
||||
private final T value;
|
||||
|
||||
private MetaObject(String key, T value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
|
||||
if (o instanceof MetaObject) {
|
||||
MetaObject that = (MetaObject) o;
|
||||
return that.key.equals(this.key) &&
|
||||
that.value.equals(this.value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MetaObject[" + key + "=" + value + "]";
|
||||
}
|
||||
}
|
||||
|
||||
// Packet meta cache
|
||||
private static Cache<Object, List<MetaObject>> META_CACHE;
|
||||
|
||||
public static <T> Optional<T> get(Object packet, String key) {
|
||||
Validate.notNull(key, "Null keys are not permitted!");
|
||||
|
||||
if (META_CACHE == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<MetaObject> meta = META_CACHE.getIfPresent(packet);
|
||||
if (meta == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
for (MetaObject object : meta) {
|
||||
if (object.key.equals(key)) {
|
||||
return Optional.of((T) object.value);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static void createCache() {
|
||||
META_CACHE = CacheBuilder
|
||||
.newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static <T> void set(Object packet, String key, T value) {
|
||||
Validate.notNull(key, "Null keys are not permitted!");
|
||||
|
||||
if (META_CACHE == null) {
|
||||
createCache();
|
||||
}
|
||||
|
||||
List<MetaObject> packetMeta;
|
||||
|
||||
try {
|
||||
packetMeta = META_CACHE.get(packet, ArrayList::new);
|
||||
} catch (ExecutionException ex) {
|
||||
// Not possible, but let's humor the array list constructor having an issue
|
||||
packetMeta = new ArrayList<>();
|
||||
}
|
||||
|
||||
packetMeta.removeIf(meta -> meta.key.equals(key));
|
||||
packetMeta.add(new MetaObject<>(key, value));
|
||||
META_CACHE.put(packet, packetMeta);
|
||||
}
|
||||
|
||||
public static <T> Optional<T> remove(Object packet, String key) {
|
||||
Validate.notNull(key, "Null keys are not permitted!");
|
||||
|
||||
if (META_CACHE == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<MetaObject> packetMeta = META_CACHE.getIfPresent(packet);
|
||||
if (packetMeta == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Optional<T> value = Optional.empty();
|
||||
Iterator<MetaObject> iter = packetMeta.iterator();
|
||||
while (iter.hasNext()) {
|
||||
MetaObject meta = iter.next();
|
||||
if (meta.key.equals(key)) {
|
||||
value = Optional.of((T) meta.value);
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+47
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
+3
-6
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,255 +1,263 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2015 dmulloy2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
/**
|
||||
* A packet represented only by its id and bytes.
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class WirePacket {
|
||||
private final int id;
|
||||
private final byte[] bytes;
|
||||
|
||||
/**
|
||||
* Constructs a new WirePacket with a given type and contents
|
||||
* @param type Type of the packet
|
||||
* @param bytes Contents of the packet
|
||||
*/
|
||||
public WirePacket(PacketType type, byte[] bytes) {
|
||||
this.id = checkNotNull(type, "type cannot be null").getCurrentId();
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new WirePacket with a given id and contents
|
||||
* @param id ID of the packet
|
||||
* @param bytes Contents of the packet
|
||||
*/
|
||||
public WirePacket(int id, byte[] bytes) {
|
||||
this.id = id;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this packet's ID
|
||||
* @return The ID
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this packet's contents as a byte array
|
||||
* @return The contents
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the id of this packet to a given output
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeId(ByteBuf output) {
|
||||
writeVarInt(output, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the contents of this packet to a given output
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeBytes(ByteBuf output) {
|
||||
checkNotNull(output, "output cannot be null!");
|
||||
output.writeBytes(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully writes the ID and contents of this packet to a given output
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeFully(ByteBuf output) {
|
||||
writeId(output);
|
||||
writeBytes(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this packet into a byte buffer
|
||||
* @return The buffer
|
||||
*/
|
||||
public ByteBuf serialize() {
|
||||
ByteBuf buffer = Unpooled.buffer();
|
||||
writeFully(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
|
||||
if (obj instanceof WirePacket) {
|
||||
WirePacket that = (WirePacket) obj;
|
||||
return this.id == that.id &&
|
||||
Arrays.equals(this.bytes, that.bytes);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(bytes);
|
||||
result = prime * result + id;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WirePacket[id=" + id + ", bytes=" + Arrays.toString(bytes) + "]";
|
||||
}
|
||||
|
||||
private static byte[] getBytes(ByteBuf buffer) {
|
||||
byte[] array = new byte[buffer.readableBytes()];
|
||||
buffer.readBytes(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WirePacket from an existing PacketContainer
|
||||
* @param packet Existing packet
|
||||
* @return The resulting WirePacket
|
||||
*/
|
||||
public static WirePacket fromPacket(PacketContainer packet) {
|
||||
int id = packet.getType().getCurrentId();
|
||||
return new WirePacket(id, bytesFromPacket(packet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a byte array from an existing PacketContainer containing all the
|
||||
* bytes from that packet
|
||||
*
|
||||
* @param packet Existing packet
|
||||
* @return The ByteBuf
|
||||
*/
|
||||
public static byte[] bytesFromPacket(PacketContainer packet) {
|
||||
checkNotNull(packet, "packet cannot be null!");
|
||||
|
||||
ByteBuf buffer = PacketContainer.createPacketBuffer();
|
||||
ByteBuf store = PacketContainer.createPacketBuffer();
|
||||
|
||||
// Read the bytes once
|
||||
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
|
||||
|
||||
try {
|
||||
write.invoke(packet.getHandle(), buffer);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to read packet contents.", ex);
|
||||
}
|
||||
|
||||
byte[] bytes = getBytes(buffer);
|
||||
|
||||
// Rewrite them to the packet to avoid issues with certain packets
|
||||
if (packet.getType() == PacketType.Play.Server.CUSTOM_PAYLOAD
|
||||
|| packet.getType() == PacketType.Play.Client.CUSTOM_PAYLOAD) {
|
||||
// Make a copy of the array before writing
|
||||
byte[] ret = Arrays.copyOf(bytes, bytes.length);
|
||||
store.writeBytes(bytes);
|
||||
|
||||
Method read = MinecraftMethods.getPacketReadByteBufMethod();
|
||||
|
||||
try {
|
||||
read.invoke(packet.getHandle(), store);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to rewrite packet contents.", ex);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WirePacket from an existing Minecraft packet
|
||||
* @param packet Existing Minecraft packet
|
||||
* @return The resulting WirePacket
|
||||
* @throws IllegalArgumentException If the packet is null or not a Minecraft packet
|
||||
*/
|
||||
public static WirePacket fromPacket(Object packet) {
|
||||
checkNotNull(packet, "packet cannot be null!");
|
||||
checkArgument(MinecraftReflection.isPacketClass(packet), "packet must be a Minecraft packet");
|
||||
|
||||
PacketType type = PacketType.fromClass(packet.getClass());
|
||||
int id = type.getCurrentId();
|
||||
|
||||
ByteBuf buffer = PacketContainer.createPacketBuffer();
|
||||
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
|
||||
|
||||
try {
|
||||
write.invoke(packet, buffer);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to serialize packet contents.", ex);
|
||||
}
|
||||
|
||||
return new WirePacket(id, getBytes(buffer));
|
||||
}
|
||||
|
||||
public static void writeVarInt(ByteBuf output, int i) {
|
||||
checkNotNull(output, "output cannot be null!");
|
||||
|
||||
while ((i & -128) != 0) {
|
||||
output.writeByte(i & 127 | 128);
|
||||
i >>>= 7;
|
||||
}
|
||||
|
||||
output.writeByte(i);
|
||||
}
|
||||
|
||||
public static int readVarInt(ByteBuf input) {
|
||||
checkNotNull(input, "input cannot be null!");
|
||||
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
byte b0;
|
||||
|
||||
do {
|
||||
b0 = input.readByte();
|
||||
i |= (b0 & 127) << j++ * 7;
|
||||
if (j > 5) {
|
||||
throw new RuntimeException("VarInt too big");
|
||||
}
|
||||
} while ((b0 & 128) == 128);
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2015 dmulloy2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
/**
|
||||
* A packet represented only by its id and bytes.
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class WirePacket {
|
||||
private final int id;
|
||||
private final byte[] bytes;
|
||||
|
||||
/**
|
||||
* Constructs a new WirePacket with a given type and contents
|
||||
* @param type Type of the packet
|
||||
* @param bytes Contents of the packet
|
||||
*/
|
||||
public WirePacket(PacketType type, byte[] bytes) {
|
||||
this.id = checkNotNull(type, "type cannot be null").getCurrentId();
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new WirePacket with a given id and contents
|
||||
* @param id ID of the packet
|
||||
* @param bytes Contents of the packet
|
||||
*/
|
||||
public WirePacket(int id, byte[] bytes) {
|
||||
this.id = id;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this packet's ID
|
||||
* @return The ID
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this packet's contents as a byte array
|
||||
* @return The contents
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the id of this packet to a given output
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeId(ByteBuf output) {
|
||||
writeVarInt(output, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the contents of this packet to a given output
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeBytes(ByteBuf output) {
|
||||
checkNotNull(output, "output cannot be null!");
|
||||
output.writeBytes(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully writes the ID and contents of this packet to a given output
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeFully(ByteBuf output) {
|
||||
writeId(output);
|
||||
writeBytes(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this packet into a byte buffer
|
||||
* @return The buffer
|
||||
*/
|
||||
public ByteBuf serialize() {
|
||||
ByteBuf buffer = Unpooled.buffer();
|
||||
writeFully(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
|
||||
if (obj instanceof WirePacket) {
|
||||
WirePacket that = (WirePacket) obj;
|
||||
return this.id == that.id &&
|
||||
Arrays.equals(this.bytes, that.bytes);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(bytes);
|
||||
result = prime * result + id;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WirePacket[id=" + id + ", bytes=" + Arrays.toString(bytes) + "]";
|
||||
}
|
||||
|
||||
private static byte[] getBytes(ByteBuf buffer) {
|
||||
byte[] array = new byte[buffer.readableBytes()];
|
||||
buffer.readBytes(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WirePacket from an existing PacketContainer
|
||||
* @param packet Existing packet
|
||||
* @return The resulting WirePacket
|
||||
*/
|
||||
public static WirePacket fromPacket(PacketContainer packet) {
|
||||
int id = packet.getType().getCurrentId();
|
||||
return new WirePacket(id, bytesFromPacket(packet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a byte array from an existing PacketContainer containing all the
|
||||
* bytes from that packet
|
||||
*
|
||||
* @param packet Existing packet
|
||||
* @return the byte array
|
||||
*/
|
||||
public static byte[] bytesFromPacket(PacketContainer packet) {
|
||||
checkNotNull(packet, "packet cannot be null!");
|
||||
|
||||
ByteBuf buffer = PacketContainer.createPacketBuffer();
|
||||
ByteBuf store = PacketContainer.createPacketBuffer();
|
||||
|
||||
// Read the bytes once
|
||||
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
|
||||
|
||||
try {
|
||||
write.invoke(packet.getHandle(), buffer);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to read packet contents.", ex);
|
||||
}
|
||||
|
||||
byte[] bytes = getBytes(buffer);
|
||||
|
||||
buffer.release();
|
||||
|
||||
// Rewrite them to the packet to avoid issues with certain packets
|
||||
if (packet.getType() == PacketType.Play.Server.CUSTOM_PAYLOAD
|
||||
|| packet.getType() == PacketType.Play.Client.CUSTOM_PAYLOAD) {
|
||||
// Make a copy of the array before writing
|
||||
byte[] ret = Arrays.copyOf(bytes, bytes.length);
|
||||
store.writeBytes(bytes);
|
||||
|
||||
Method read = MinecraftMethods.getPacketReadByteBufMethod();
|
||||
|
||||
try {
|
||||
read.invoke(packet.getHandle(), store);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to rewrite packet contents.", ex);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
store.release();
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WirePacket from an existing Minecraft packet
|
||||
* @param packet Existing Minecraft packet
|
||||
* @return The resulting WirePacket
|
||||
* @throws IllegalArgumentException If the packet is null or not a Minecraft packet
|
||||
*/
|
||||
public static WirePacket fromPacket(Object packet) {
|
||||
checkNotNull(packet, "packet cannot be null!");
|
||||
checkArgument(MinecraftReflection.isPacketClass(packet), "packet must be a Minecraft packet");
|
||||
|
||||
PacketType type = PacketType.fromClass(packet.getClass());
|
||||
int id = type.getCurrentId();
|
||||
|
||||
ByteBuf buffer = PacketContainer.createPacketBuffer();
|
||||
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
|
||||
|
||||
try {
|
||||
write.invoke(packet, buffer);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to serialize packet contents.", ex);
|
||||
}
|
||||
|
||||
byte[] bytes = getBytes(buffer);
|
||||
|
||||
buffer.release();
|
||||
|
||||
return new WirePacket(id, bytes);
|
||||
}
|
||||
|
||||
public static void writeVarInt(ByteBuf output, int i) {
|
||||
checkNotNull(output, "output cannot be null!");
|
||||
|
||||
while ((i & -128) != 0) {
|
||||
output.writeByte(i & 127 | 128);
|
||||
i >>>= 7;
|
||||
}
|
||||
|
||||
output.writeByte(i);
|
||||
}
|
||||
|
||||
public static int readVarInt(ByteBuf input) {
|
||||
checkNotNull(input, "input cannot be null!");
|
||||
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
byte b0;
|
||||
|
||||
do {
|
||||
b0 = input.readByte();
|
||||
i |= (b0 & 127) << j++ * 7;
|
||||
if (j > 5) {
|
||||
throw new RuntimeException("VarInt too big");
|
||||
}
|
||||
} while ((b0 & 128) == 128);
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
+2
-20
@@ -171,16 +171,7 @@ public class PacketRegistry {
|
||||
initialize();
|
||||
NETTY.synchronize();
|
||||
|
||||
Set<PacketType> types = new HashSet<>();
|
||||
|
||||
// Filter out unsupported packets
|
||||
for (PacketType type : NETTY.getServerPackets()) {
|
||||
if (!type.isDeprecated()) {
|
||||
types.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
return NETTY.getServerPackets();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,16 +198,7 @@ public class PacketRegistry {
|
||||
initialize();
|
||||
NETTY.synchronize();
|
||||
|
||||
Set<PacketType> types = new HashSet<>();
|
||||
|
||||
// Filter out unsupported packets
|
||||
for (PacketType type : NETTY.getClientPackets()) {
|
||||
if (!type.isDeprecated()) {
|
||||
types.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
return NETTY.getClientPackets();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -4,10 +4,10 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents an injector that only gives access to a player's socket.
|
||||
*
|
||||
+6
-6
@@ -1,19 +1,19 @@
|
||||
package com.comphenix.protocol.injector.server;
|
||||
|
||||
/**
|
||||
* Able to store a socket injector.
|
||||
* A temporary player created by ProtocolLib when a true player instance does not exist.
|
||||
* <p>
|
||||
* A necessary hack.
|
||||
* @author Kristian
|
||||
* Also able to store a socket injector
|
||||
* </p>
|
||||
*/
|
||||
class InjectorContainer {
|
||||
public class TemporaryPlayer {
|
||||
private volatile SocketInjector injector;
|
||||
|
||||
public SocketInjector getInjector() {
|
||||
SocketInjector getInjector() {
|
||||
return injector;
|
||||
}
|
||||
|
||||
public void setInjector(SocketInjector injector) {
|
||||
void setInjector(SocketInjector injector) {
|
||||
if (injector == null)
|
||||
throw new IllegalArgumentException("Injector cannot be NULL.");
|
||||
this.injector = injector;
|
||||
@@ -4,16 +4,11 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.cglib.asm.ClassReader;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
import net.sf.cglib.asm.Opcodes;
|
||||
import net.sf.cglib.asm.Type;
|
||||
|
||||
import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod.AsmOpcodes;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.sf.cglib.asm.*;
|
||||
|
||||
public class ClassAnalyser {
|
||||
/**
|
||||
* Represents a method in ASM.
|
||||
@@ -31,11 +26,11 @@ public class ClassAnalyser {
|
||||
|
||||
public static AsmOpcodes fromIntOpcode(int opcode) {
|
||||
switch (opcode) {
|
||||
case Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL;
|
||||
case Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL;
|
||||
case Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC;
|
||||
case Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE;
|
||||
case Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC;
|
||||
case $Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL;
|
||||
case $Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL;
|
||||
case $Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC;
|
||||
case $Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE;
|
||||
case $Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC;
|
||||
default: throw new IllegalArgumentException("Unknown opcode: " + opcode);
|
||||
}
|
||||
}
|
||||
@@ -109,30 +104,29 @@ public class ClassAnalyser {
|
||||
* @return The method calls.
|
||||
* @throws IOException Cannot access the parent class.
|
||||
*/
|
||||
public List<AsmMethod> getMethodCalls(Class<?> clazz, Method method) throws IOException {
|
||||
final ClassReader reader = new ClassReader(clazz.getCanonicalName());
|
||||
private List<AsmMethod> getMethodCalls(Class<?> clazz, Method method) throws IOException {
|
||||
final $ClassReader reader = new $ClassReader(clazz.getCanonicalName());
|
||||
final List<AsmMethod> output = Lists.newArrayList();
|
||||
|
||||
// The method we are looking for
|
||||
final String methodName = method.getName();
|
||||
final String methodDescription = Type.getMethodDescriptor(method);
|
||||
|
||||
reader.accept(new EmptyClassVisitor() {
|
||||
final String methodDescription = $Type.getMethodDescriptor(method);
|
||||
|
||||
reader.accept(new $ClassVisitor($Opcodes.ASM5) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
// Check method
|
||||
public $MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
if (methodName.equals(name) && methodDescription.equals(desc)) {
|
||||
return new EmptyMethodVisitor() {
|
||||
return new $MethodVisitor($Opcodes.ASM5) {
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean flag) {
|
||||
output.add(new AsmMethod(AsmOpcodes.fromIntOpcode(opcode), owner, methodName, desc));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}, ClassReader.EXPAND_FRAMES);
|
||||
}, $ClassReader.EXPAND_FRAMES);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -21,32 +21,30 @@ package com.comphenix.protocol.reflect;
|
||||
* Interface that converts generic objects into types and back.
|
||||
*
|
||||
* @author Kristian
|
||||
* @param <TType> The specific type.
|
||||
* @param <T> The specific type.
|
||||
*/
|
||||
public interface EquivalentConverter<TType> {
|
||||
/**
|
||||
* Retrieve a copy of the specific type using an instance of the generic type.
|
||||
* <p>
|
||||
* This is usually a wrapper type in the Bukkit API.
|
||||
* @param generic - the generic type.
|
||||
* @return The new specific type.
|
||||
*/
|
||||
public TType getSpecific(Object generic);
|
||||
|
||||
public interface EquivalentConverter<T> {
|
||||
/**
|
||||
* Retrieve a copy of the generic type from a specific type.
|
||||
* <p>
|
||||
* This is usually a native net.minecraft.server type in Minecraft.
|
||||
* @param genericType - class or super class of the generic type.
|
||||
* @param specific - the specific type we need to copy.
|
||||
* @return A copy of the specific type.
|
||||
*/
|
||||
public Object getGeneric(Class<?> genericType, TType specific);
|
||||
|
||||
Object getGeneric(T specific);
|
||||
|
||||
/**
|
||||
* Due to type erasion, we need to explicitly keep a reference to the specific type.
|
||||
* Retrieve a copy of the specific type using an instance of the generic type.
|
||||
* <p>
|
||||
* This is usually a wrapper type in the Bukkit API or ProtocolLib API.
|
||||
* @param generic - the generic type.
|
||||
* @return The new specific type.
|
||||
*/
|
||||
T getSpecific(Object generic);
|
||||
|
||||
/**
|
||||
* Due to type erasure, we need to explicitly keep a reference to the specific type.
|
||||
* @return The specific type.
|
||||
*/
|
||||
public Class<TType> getSpecificType();
|
||||
}
|
||||
|
||||
Class<T> getSpecificType();
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -19,10 +19,7 @@ package com.comphenix.protocol.reflect;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@@ -263,6 +260,24 @@ public class StructureModifier<TField> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the value of a field only if it exists. If the field does not exist, an empty {@link Optional} is returned.
|
||||
* <p>
|
||||
* This method has the same functionality as {@link #readSafely(int)}, but enforces null checks by way of an Optional.
|
||||
* It will eventually become the preferred method of reading fields.
|
||||
*
|
||||
* @param fieldIndex index of the field
|
||||
* @return An optional that may contain the value of the field
|
||||
* @see #readSafely(int)
|
||||
*/
|
||||
public Optional<TField> optionRead(int fieldIndex) {
|
||||
try {
|
||||
return Optional.ofNullable(read(fieldIndex));
|
||||
} catch (FieldAccessException ex) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not a field is read-only (final).
|
||||
@@ -352,7 +367,7 @@ public class StructureModifier<TField> {
|
||||
throw new FieldAccessException(String.format("Field index out of bounds. (Index: %s, Size: %s)", fieldIndex, data.size()));
|
||||
|
||||
// Use the converter, if it exists
|
||||
Object obj = needConversion() ? converter.getGeneric(getFieldType(fieldIndex), value) : value;
|
||||
Object obj = needConversion() ? converter.getGeneric(value) : value;
|
||||
|
||||
try {
|
||||
FieldUtils.writeField(data.get(fieldIndex), target, obj, true);
|
||||
@@ -453,6 +468,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 +488,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 +502,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
|
||||
|
||||
+8
-2
@@ -254,10 +254,16 @@ public class AggregateCloner implements Cloner {
|
||||
|
||||
// Make sure the object is valid
|
||||
if (index < cloners.size()) {
|
||||
return cloners.get(index).clone(source);
|
||||
Cloner cloner = cloners.get(index);
|
||||
|
||||
try {
|
||||
return cloner.clone(source);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to clone " + source + " (" + source.getClass() + ") with " + cloner, ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Damn - failure
|
||||
throw new IllegalArgumentException("Cannot clone " + source + ": No cloner is suitable.");
|
||||
throw new IllegalArgumentException("Cannot clone " + source + " ( " + source.getClass() + "): No cloner is suitable.");
|
||||
}
|
||||
}
|
||||
+48
-81
@@ -21,17 +21,14 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.ChunkPosition;
|
||||
import com.comphenix.protocol.wrappers.MinecraftKey;
|
||||
import com.comphenix.protocol.wrappers.WrappedBlockData;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.comphenix.protocol.wrappers.WrappedServerPing;
|
||||
import com.comphenix.protocol.wrappers.*;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
@@ -40,58 +37,56 @@ import com.google.common.collect.Maps;
|
||||
* @author Kristian
|
||||
*/
|
||||
public class BukkitCloner implements Cloner {
|
||||
// List of classes we support
|
||||
private final Map<Integer, Class<?>> clonableClasses = Maps.newConcurrentMap();
|
||||
private static final Map<Class<?>, Function<Object, Object>> CLONERS = Maps.newConcurrentMap();
|
||||
|
||||
public BukkitCloner() {
|
||||
addClass(0, MinecraftReflection.getItemStackClass());
|
||||
addClass(1, MinecraftReflection.getDataWatcherClass());
|
||||
|
||||
// Try to add position classes
|
||||
private static void fromWrapper(Supplier<Class<?>> getClass, Function<Object, ClonableWrapper> fromHandle) {
|
||||
try {
|
||||
addClass(2, MinecraftReflection.getBlockPositionClass());
|
||||
} catch (Throwable ex) {
|
||||
}
|
||||
|
||||
try {
|
||||
addClass(3, MinecraftReflection.getChunkPositionClass());
|
||||
} catch (Throwable ex) {
|
||||
}
|
||||
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
addClass(4, MinecraftReflection.getServerPingClass());
|
||||
}
|
||||
|
||||
if (MinecraftReflection.watcherObjectExists()) {
|
||||
addClass(5, MinecraftReflection.getDataWatcherSerializerClass());
|
||||
addClass(6, MinecraftReflection.getMinecraftKeyClass());
|
||||
}
|
||||
|
||||
try {
|
||||
addClass(7, MinecraftReflection.getIBlockDataClass());
|
||||
} catch (Throwable ex) {
|
||||
}
|
||||
|
||||
try {
|
||||
addClass(8, MinecraftReflection.getNonNullListClass());
|
||||
} catch (Throwable ex) {
|
||||
}
|
||||
Class<?> nmsClass = getClass.get();
|
||||
if (nmsClass != null) {
|
||||
CLONERS.put(nmsClass, nmsObject -> fromHandle.apply(nmsObject).deepClone().getHandle());
|
||||
}
|
||||
} catch (RuntimeException ignored) { }
|
||||
}
|
||||
|
||||
private void addClass(int id, Class<?> clazz) {
|
||||
if (clazz != null)
|
||||
clonableClasses.put(id, clazz);
|
||||
private static void fromConverter(Supplier<Class<?>> getClass, EquivalentConverter converter) {
|
||||
try {
|
||||
Class<?> nmsClass = getClass.get();
|
||||
if (nmsClass != null) {
|
||||
CLONERS.put(nmsClass, nmsObject -> converter.getGeneric(converter.getSpecific(nmsObject)));
|
||||
}
|
||||
} catch (RuntimeException ignored) { }
|
||||
}
|
||||
|
||||
private int findMatchingClass(Class<?> type) {
|
||||
// See if is a subclass of any of our supported superclasses
|
||||
for (Entry<Integer, Class<?>> entry : clonableClasses.entrySet()) {
|
||||
if (entry.getValue().isAssignableFrom(type)) {
|
||||
return entry.getKey();
|
||||
private static void fromManual(Supplier<Class<?>> getClass, Function<Object, Object> cloner) {
|
||||
try {
|
||||
Class<?> nmsClass = getClass.get();
|
||||
if (nmsClass != null) {
|
||||
CLONERS.put(nmsClass, cloner);
|
||||
}
|
||||
} catch (RuntimeException ignored) { }
|
||||
}
|
||||
|
||||
static {
|
||||
fromManual(MinecraftReflection::getItemStackClass, source ->
|
||||
MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone()));
|
||||
fromWrapper(MinecraftReflection::getDataWatcherClass, WrappedDataWatcher::new);
|
||||
fromConverter(MinecraftReflection::getBlockPositionClass, BlockPosition.getConverter());
|
||||
fromConverter(MinecraftReflection::getChunkPositionClass, ChunkPosition.getConverter());
|
||||
fromWrapper(MinecraftReflection::getServerPingClass, WrappedServerPing::fromHandle);
|
||||
fromConverter(MinecraftReflection::getMinecraftKeyClass, MinecraftKey.getConverter());
|
||||
fromWrapper(MinecraftReflection::getIBlockDataClass, WrappedBlockData::fromHandle);
|
||||
fromManual(MinecraftReflection::getNonNullListClass, source -> nonNullListCloner().clone(source));
|
||||
fromWrapper(MinecraftReflection::getNBTBaseClass, NbtFactory::fromNMS);
|
||||
}
|
||||
|
||||
private Function<Object, Object> findCloner(Class<?> type) {
|
||||
for (Entry<Class<?>, Function<Object, Object>> entry : CLONERS.entrySet()) {
|
||||
if (entry.getKey().isAssignableFrom(type)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,7 +94,7 @@ public class BukkitCloner implements Cloner {
|
||||
if (source == null)
|
||||
return false;
|
||||
|
||||
return findMatchingClass(source.getClass()) >= 0;
|
||||
return findCloner(source.getClass()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -107,40 +102,12 @@ public class BukkitCloner implements Cloner {
|
||||
if (source == null)
|
||||
throw new IllegalArgumentException("source cannot be NULL.");
|
||||
|
||||
// Convert to a wrapper
|
||||
switch (findMatchingClass(source.getClass())) {
|
||||
case 0:
|
||||
return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone());
|
||||
case 1:
|
||||
EquivalentConverter<WrappedDataWatcher> dataConverter = BukkitConverters.getDataWatcherConverter();
|
||||
return dataConverter.getGeneric(clonableClasses.get(1), dataConverter.getSpecific(source).deepClone());
|
||||
case 2:
|
||||
EquivalentConverter<BlockPosition> blockConverter = BlockPosition.getConverter();
|
||||
return blockConverter.getGeneric(clonableClasses.get(2), blockConverter.getSpecific(source));
|
||||
case 3:
|
||||
EquivalentConverter<ChunkPosition> chunkConverter = ChunkPosition.getConverter();
|
||||
return chunkConverter.getGeneric(clonableClasses.get(3), chunkConverter.getSpecific(source));
|
||||
case 4:
|
||||
EquivalentConverter<WrappedServerPing> serverConverter = BukkitConverters.getWrappedServerPingConverter();
|
||||
return serverConverter.getGeneric(clonableClasses.get(4), serverConverter.getSpecific(source).deepClone());
|
||||
case 5:
|
||||
return source;
|
||||
case 6:
|
||||
EquivalentConverter<MinecraftKey> keyConverter = MinecraftKey.getConverter();
|
||||
return keyConverter.getGeneric(clonableClasses.get(6), keyConverter.getSpecific(source));
|
||||
case 7:
|
||||
EquivalentConverter<WrappedBlockData> blockDataConverter = BukkitConverters.getWrappedBlockDataConverter();
|
||||
return blockDataConverter.getGeneric(clonableClasses.get(7), blockDataConverter.getSpecific(source).deepClone());
|
||||
case 8:
|
||||
return nonNullListCloner().clone(source);
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass());
|
||||
}
|
||||
return findCloner(source.getClass()).apply(source);
|
||||
}
|
||||
|
||||
private static Constructor<?> nonNullList = null;
|
||||
|
||||
private static final Cloner nonNullListCloner() {
|
||||
private static Cloner nonNullListCloner() {
|
||||
return new Cloner() {
|
||||
@Override
|
||||
public boolean canClone(Object source) {
|
||||
|
||||
+77
-57
@@ -20,10 +20,13 @@ package com.comphenix.protocol.reflect.cloning;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
|
||||
/**
|
||||
* Attempts to clone collection and array classes.
|
||||
*
|
||||
@@ -48,72 +51,89 @@ public class CollectionCloner implements Cloner {
|
||||
Class<?> clazz = source.getClass();
|
||||
return Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz) || clazz.isArray();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object clone(Object source) {
|
||||
if (source == null)
|
||||
throw new IllegalArgumentException("source cannot be NULL.");
|
||||
|
||||
Class<?> clazz = source.getClass();
|
||||
|
||||
if (source instanceof Collection) {
|
||||
Collection<Object> copy = cloneConstructor(Collection.class, clazz, source);
|
||||
try {
|
||||
if (source instanceof Collection) {
|
||||
Collection<Object> copy = cloneConstructor(Collection.class, clazz, source);
|
||||
|
||||
// Next, clone each element in the collection
|
||||
try {
|
||||
copy.clear();
|
||||
|
||||
for (Object element : (Collection<Object>) source) {
|
||||
copy.add(getClone(element, source));
|
||||
// Next, clone each element in the collection
|
||||
try {
|
||||
copy.clear();
|
||||
|
||||
for (Object element : (Collection<Object>) source) {
|
||||
copy.add(getClone(element, source));
|
||||
}
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Immutable - we can't do much about that
|
||||
}
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Immutable - we can't do much about that
|
||||
}
|
||||
return copy;
|
||||
|
||||
} else if (source instanceof Map) {
|
||||
Map<Object, Object> copy = cloneConstructor(Map.class, clazz, source);
|
||||
|
||||
// Next, clone each element in the collection
|
||||
try {
|
||||
copy.clear();
|
||||
|
||||
for (Entry<Object, Object> element : ((Map<Object, Object>) source).entrySet()) {
|
||||
Object key = getClone(element.getKey(), source);
|
||||
Object value = getClone(element.getValue(), source);
|
||||
copy.put(key, value);
|
||||
return copy;
|
||||
|
||||
} else if (source instanceof Map) {
|
||||
Map<Object, Object> copy;
|
||||
|
||||
if (source instanceof BiMap) {
|
||||
BiMap original = (BiMap) source;
|
||||
if (clazz.getDeclaringClass() != null) {
|
||||
Method create = clazz.getDeclaringClass().getMethod("create", int.class);
|
||||
copy = ((BiMap) create.invoke(null, original.size())).inverse();
|
||||
} else {
|
||||
Method create = clazz.getMethod("create", int.class);
|
||||
copy = (Map) create.invoke(null, original.size());
|
||||
}
|
||||
} else {
|
||||
copy = cloneConstructor(Map.class, clazz, source);
|
||||
}
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Immutable - we can't do much about that
|
||||
|
||||
// Next, clone each element in the collection
|
||||
try {
|
||||
copy.clear();
|
||||
|
||||
for (Entry<Object, Object> element : ((Map<Object, Object>) source).entrySet()) {
|
||||
Object key = getClone(element.getKey(), source);
|
||||
Object value = getClone(element.getValue(), source);
|
||||
copy.put(key, value);
|
||||
}
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Immutable - we can't do much about that
|
||||
}
|
||||
return copy;
|
||||
|
||||
} else if (clazz.isArray()) {
|
||||
// Get the length
|
||||
int length = Array.getLength(source);
|
||||
Class<?> component = clazz.getComponentType();
|
||||
|
||||
// Can we speed things up by making a shallow copy instead?
|
||||
if (ImmutableDetector.isImmutable(component)) {
|
||||
return clonePrimitive(component, source);
|
||||
}
|
||||
|
||||
// Create a new copy
|
||||
Object copy = Array.newInstance(clazz.getComponentType(), length);
|
||||
|
||||
// Set each element
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object element = Array.get(source, i);
|
||||
|
||||
if (defaultCloner.canClone(element))
|
||||
Array.set(copy, i, defaultCloner.clone(element));
|
||||
else
|
||||
throw new IllegalArgumentException("Cannot clone " + element + " in array " + source);
|
||||
}
|
||||
|
||||
// And we're done
|
||||
return copy;
|
||||
}
|
||||
return copy;
|
||||
|
||||
} else if (clazz.isArray()) {
|
||||
// Get the length
|
||||
int lenght = Array.getLength(source);
|
||||
Class<?> component = clazz.getComponentType();
|
||||
|
||||
// Can we speed things up by making a shallow copy instead?
|
||||
if (ImmutableDetector.isImmutable(component)) {
|
||||
return clonePrimitive(component, source);
|
||||
}
|
||||
|
||||
// Create a new copy
|
||||
Object copy = Array.newInstance(clazz.getComponentType(), lenght);
|
||||
|
||||
// Set each element
|
||||
for (int i = 0; i < lenght; i++) {
|
||||
Object element = Array.get(source, i);
|
||||
|
||||
if (defaultCloner.canClone(element))
|
||||
Array.set(copy, i, defaultCloner.clone(element));
|
||||
else
|
||||
throw new IllegalArgumentException("Cannot clone " + element + " in array " + source);
|
||||
}
|
||||
|
||||
// And we're done
|
||||
return copy;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to clone " + source + " (" + source.getClass() + ")", ex);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(source + " is not an array nor a Collection.");
|
||||
@@ -171,7 +191,7 @@ public class CollectionCloner implements Cloner {
|
||||
private <T> T cloneConstructor(Class<?> superclass, Class<?> clazz, Object source) {
|
||||
// Not all collections or maps implement "clone", but most *do* implement the "copy constructor" pattern
|
||||
try {
|
||||
Constructor<?> constructCopy = clazz.getConstructor(Collection.class);
|
||||
Constructor<?> constructCopy = clazz.getConstructor(superclass);
|
||||
return (T) constructCopy.newInstance(source);
|
||||
} catch (NoSuchMethodException e) {
|
||||
if (source instanceof Serializable)
|
||||
@@ -186,7 +206,7 @@ public class CollectionCloner implements Cloner {
|
||||
/**
|
||||
* Clone an object by calling "clone" using reflection.
|
||||
* @param clazz - the class type.
|
||||
* @param obj - the object to clone.
|
||||
* @param source - the object to clone.
|
||||
* @return The cloned object.
|
||||
*/
|
||||
private Object cloneObject(Class<?> clazz, Object source) {
|
||||
|
||||
+3
-3
@@ -6,14 +6,14 @@ package com.comphenix.protocol.reflect.cloning;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
/**
|
||||
* A cloner that can clone Optional objects
|
||||
* A cloner that can clone Guava Optional objects
|
||||
* @author dmulloy2
|
||||
*/
|
||||
|
||||
public class OptionalCloner implements Cloner {
|
||||
public class GuavaOptionalCloner implements Cloner {
|
||||
protected Cloner wrapped;
|
||||
|
||||
public OptionalCloner(Cloner wrapped) {
|
||||
public GuavaOptionalCloner(Cloner wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
+62
-29
@@ -26,11 +26,16 @@ import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.primitives.Primitives;
|
||||
|
||||
/**
|
||||
@@ -42,19 +47,45 @@ import com.google.common.primitives.Primitives;
|
||||
*/
|
||||
public class ImmutableDetector implements Cloner {
|
||||
// Notable immutable classes we might encounter
|
||||
private static final Class<?>[] immutableClasses = {
|
||||
StackTraceElement.class, BigDecimal.class,
|
||||
BigInteger.class, Locale.class, UUID.class,
|
||||
URL.class, URI.class, Inet4Address.class,
|
||||
Inet6Address.class, InetSocketAddress.class,
|
||||
SecretKey.class, PublicKey.class
|
||||
};
|
||||
private static final Set<Class<?>> immutableClasses = ImmutableSet.of(
|
||||
StackTraceElement.class, BigDecimal.class,
|
||||
BigInteger.class, Locale.class, UUID.class,
|
||||
URL.class, URI.class, Inet4Address.class,
|
||||
Inet6Address.class, InetSocketAddress.class,
|
||||
SecretKey.class, PublicKey.class
|
||||
);
|
||||
|
||||
private static final Set<Class<?>> immutableNMS = Sets.newConcurrentHashSet();
|
||||
|
||||
static {
|
||||
add(MinecraftReflection::getGameProfileClass);
|
||||
add(MinecraftReflection::getDataWatcherSerializerClass);
|
||||
add(() -> MinecraftReflection.getMinecraftClass("SoundEffect"));
|
||||
add(MinecraftReflection::getBlockClass);
|
||||
add(MinecraftReflection::getItemClass);
|
||||
|
||||
if (MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
|
||||
add(() -> MinecraftReflection.getMinecraftClass("Particle"));
|
||||
add(MinecraftReflection::getFluidTypeClass);
|
||||
add(MinecraftReflection::getParticleTypeClass);
|
||||
}
|
||||
}
|
||||
|
||||
private static void add(Supplier<Class<?>> getClass) {
|
||||
try {
|
||||
Class<?> clazz = getClass.get();
|
||||
if (clazz != null) {
|
||||
immutableNMS.add(clazz);
|
||||
}
|
||||
} catch (RuntimeException ignored) { }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canClone(Object source) {
|
||||
// Don't accept NULL
|
||||
if (source == null)
|
||||
if (source == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isImmutable(source.getClass());
|
||||
}
|
||||
@@ -66,32 +97,31 @@ public class ImmutableDetector implements Cloner {
|
||||
*/
|
||||
public static boolean isImmutable(Class<?> type) {
|
||||
// Cases that are definitely not true
|
||||
if (type.isArray())
|
||||
if (type.isArray()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All primitive types
|
||||
if (Primitives.isWrapperType(type) || String.class.equals(type))
|
||||
if (Primitives.isWrapperType(type) || String.class.equals(type)) {
|
||||
return true;
|
||||
|
||||
// May not be true, but if so, that kind of code is broken anyways
|
||||
if (isEnumWorkaround(type))
|
||||
return true;
|
||||
|
||||
for (Class<?> clazz : immutableClasses)
|
||||
if (clazz.equals(type))
|
||||
return true;
|
||||
|
||||
// Check for known immutable classes in 1.7.2
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
if (type.equals(MinecraftReflection.getGameProfileClass())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for known immutable classes in 1.9
|
||||
if (MinecraftReflection.watcherObjectExists()) {
|
||||
if (type.equals(MinecraftReflection.getDataWatcherSerializerClass())
|
||||
|| type.equals(MinecraftReflection.getMinecraftClass("SoundEffect"))) {
|
||||
// May not be true, but if so, that kind of code is broken anyways
|
||||
if (isEnumWorkaround(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// No good way to clone lambdas
|
||||
if (type.getName().contains("$$Lambda$")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (immutableClasses.contains(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Class<?> clazz : immutableNMS) {
|
||||
if (MinecraftReflection.is(clazz, type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -103,10 +133,13 @@ public class ImmutableDetector implements Cloner {
|
||||
// This is just great. Just great.
|
||||
private static boolean isEnumWorkaround(Class<?> enumClass) {
|
||||
while (enumClass != null) {
|
||||
if (enumClass.isEnum())
|
||||
if (enumClass.isEnum()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
enumClass = enumClass.getSuperclass();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* (c) 2018 dmulloy2
|
||||
*/
|
||||
package com.comphenix.protocol.reflect.cloning;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A cloner that can clone Java Optional objects
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class JavaOptionalCloner implements Cloner {
|
||||
protected Cloner wrapped;
|
||||
|
||||
public JavaOptionalCloner(Cloner wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canClone(Object source) {
|
||||
return source instanceof Optional;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone(Object source) {
|
||||
Optional<?> optional = (Optional<?>) source;
|
||||
return optional.map(o -> wrapped.clone(o));
|
||||
}
|
||||
|
||||
public Cloner getWrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
}
|
||||
+81
-81
@@ -17,25 +17,25 @@
|
||||
|
||||
package com.comphenix.protocol.reflect.compiler;
|
||||
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
import net.sf.cglib.asm.Opcodes;
|
||||
import net.sf.cglib.asm.Type;
|
||||
import net.sf.cglib.asm.$MethodVisitor;
|
||||
import net.sf.cglib.asm.$Opcodes;
|
||||
import net.sf.cglib.asm.$Type;
|
||||
|
||||
/**
|
||||
* Used by the compiler to automatically box and unbox values.
|
||||
*/
|
||||
class BoxingHelper {
|
||||
|
||||
private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");
|
||||
private final static Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean");
|
||||
private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short");
|
||||
private final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character");
|
||||
private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer");
|
||||
private final static Type FLOAT_TYPE = Type.getObjectType("java/lang/Float");
|
||||
private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long");
|
||||
private final static Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double");
|
||||
private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number");
|
||||
private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
|
||||
private final static $Type BYTE_$Type = $Type.getObjectType("java/lang/Byte");
|
||||
private final static $Type BOOLEAN_$Type = $Type.getObjectType("java/lang/Boolean");
|
||||
private final static $Type SHORT_$Type = $Type.getObjectType("java/lang/Short");
|
||||
private final static $Type CHARACTER_$Type = $Type.getObjectType("java/lang/Character");
|
||||
private final static $Type INTEGER_$Type = $Type.getObjectType("java/lang/Integer");
|
||||
private final static $Type FLOAT_$Type = $Type.getObjectType("java/lang/Float");
|
||||
private final static $Type LONG_$Type = $Type.getObjectType("java/lang/Long");
|
||||
private final static $Type DOUBLE_$Type = $Type.getObjectType("java/lang/Double");
|
||||
private final static $Type NUMBER_$Type = $Type.getObjectType("java/lang/Number");
|
||||
private final static $Type OBJECT_$Type = $Type.getObjectType("java/lang/Object");
|
||||
|
||||
private final static MethodDescriptor BOOLEAN_VALUE = MethodDescriptor.getMethod("boolean booleanValue()");
|
||||
private final static MethodDescriptor CHAR_VALUE = MethodDescriptor.getMethod("char charValue()");
|
||||
@@ -44,9 +44,9 @@ class BoxingHelper {
|
||||
private final static MethodDescriptor LONG_VALUE = MethodDescriptor.getMethod("long longValue()");
|
||||
private final static MethodDescriptor DOUBLE_VALUE = MethodDescriptor.getMethod("double doubleValue()");
|
||||
|
||||
private MethodVisitor mv;
|
||||
private $MethodVisitor mv;
|
||||
|
||||
public BoxingHelper(MethodVisitor mv) {
|
||||
public BoxingHelper($MethodVisitor mv) {
|
||||
this.mv = mv;
|
||||
}
|
||||
|
||||
@@ -54,42 +54,42 @@ class BoxingHelper {
|
||||
* Generates the instructions to box the top stack value. This value is
|
||||
* replaced by its boxed equivalent on top of the stack.
|
||||
*
|
||||
* @param type the type of the top stack value.
|
||||
* @param type the $Type of the top stack value.
|
||||
*/
|
||||
public void box(final Type type){
|
||||
if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
|
||||
public void box(final $Type type){
|
||||
if(type.getSort() == $Type.OBJECT || type.getSort() == $Type.ARRAY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(type == Type.VOID_TYPE) {
|
||||
if(type == $Type.VOID_TYPE) {
|
||||
push((String) null);
|
||||
} else {
|
||||
Type boxed = type;
|
||||
$Type boxed = type;
|
||||
|
||||
switch(type.getSort()) {
|
||||
case Type.BYTE:
|
||||
boxed = BYTE_TYPE;
|
||||
case $Type.BYTE:
|
||||
boxed = BYTE_$Type;
|
||||
break;
|
||||
case Type.BOOLEAN:
|
||||
boxed = BOOLEAN_TYPE;
|
||||
case $Type.BOOLEAN:
|
||||
boxed = BOOLEAN_$Type;
|
||||
break;
|
||||
case Type.SHORT:
|
||||
boxed = SHORT_TYPE;
|
||||
case $Type.SHORT:
|
||||
boxed = SHORT_$Type;
|
||||
break;
|
||||
case Type.CHAR:
|
||||
boxed = CHARACTER_TYPE;
|
||||
case $Type.CHAR:
|
||||
boxed = CHARACTER_$Type;
|
||||
break;
|
||||
case Type.INT:
|
||||
boxed = INTEGER_TYPE;
|
||||
case $Type.INT:
|
||||
boxed = INTEGER_$Type;
|
||||
break;
|
||||
case Type.FLOAT:
|
||||
boxed = FLOAT_TYPE;
|
||||
case $Type.FLOAT:
|
||||
boxed = FLOAT_$Type;
|
||||
break;
|
||||
case Type.LONG:
|
||||
boxed = LONG_TYPE;
|
||||
case $Type.LONG:
|
||||
boxed = LONG_$Type;
|
||||
break;
|
||||
case Type.DOUBLE:
|
||||
boxed = DOUBLE_TYPE;
|
||||
case $Type.DOUBLE:
|
||||
boxed = DOUBLE_$Type;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -105,46 +105,46 @@ class BoxingHelper {
|
||||
swap();
|
||||
}
|
||||
|
||||
invokeConstructor(boxed, new MethodDescriptor("<init>", Type.VOID_TYPE, new Type[] {type}));
|
||||
invokeConstructor(boxed, new MethodDescriptor("<init>", $Type.VOID_TYPE, new $Type[] {type}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the instruction to invoke a constructor.
|
||||
*
|
||||
* @param type the class in which the constructor is defined.
|
||||
* @param $Type the class in which the constructor is defined.
|
||||
* @param method the constructor to be invoked.
|
||||
*/
|
||||
public void invokeConstructor(final Type type, final MethodDescriptor method){
|
||||
invokeInsn(Opcodes.INVOKESPECIAL, type, method);
|
||||
public void invokeConstructor(final $Type $Type, final MethodDescriptor method){
|
||||
invokeInsn($Opcodes.INVOKESPECIAL, $Type, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a DUP_X1 instruction.
|
||||
*/
|
||||
public void dupX1(){
|
||||
mv.visitInsn(Opcodes.DUP_X1);
|
||||
mv.visitInsn($Opcodes.DUP_X1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a DUP_X2 instruction.
|
||||
*/
|
||||
public void dupX2(){
|
||||
mv.visitInsn(Opcodes.DUP_X2);
|
||||
mv.visitInsn($Opcodes.DUP_X2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a POP instruction.
|
||||
*/
|
||||
public void pop(){
|
||||
mv.visitInsn(Opcodes.POP);
|
||||
mv.visitInsn($Opcodes.POP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a SWAP instruction.
|
||||
*/
|
||||
public void swap(){
|
||||
mv.visitInsn(Opcodes.SWAP);
|
||||
mv.visitInsn($Opcodes.SWAP);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,11 +163,11 @@ class BoxingHelper {
|
||||
*/
|
||||
public void push(final int value) {
|
||||
if (value >= -1 && value <= 5) {
|
||||
mv.visitInsn(Opcodes.ICONST_0 + value);
|
||||
mv.visitInsn($Opcodes.ICONST_0 + value);
|
||||
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.BIPUSH, value);
|
||||
mv.visitIntInsn($Opcodes.BIPUSH, value);
|
||||
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.SIPUSH, value);
|
||||
mv.visitIntInsn($Opcodes.SIPUSH, value);
|
||||
} else {
|
||||
mv.visitLdcInsn(new Integer(value));
|
||||
}
|
||||
@@ -176,10 +176,10 @@ class BoxingHelper {
|
||||
/**
|
||||
* Generates the instruction to create a new object.
|
||||
*
|
||||
* @param type the class of the object to be created.
|
||||
* @param $Type the class of the object to be created.
|
||||
*/
|
||||
public void newInstance(final Type type){
|
||||
typeInsn(Opcodes.NEW, type);
|
||||
public void newInstance(final $Type $Type){
|
||||
$TypeInsn($Opcodes.NEW, $Type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +189,7 @@ class BoxingHelper {
|
||||
*/
|
||||
public void push(final String value) {
|
||||
if (value == null) {
|
||||
mv.visitInsn(Opcodes.ACONST_NULL);
|
||||
mv.visitInsn($Opcodes.ACONST_NULL);
|
||||
} else {
|
||||
mv.visitLdcInsn(value);
|
||||
}
|
||||
@@ -200,35 +200,35 @@ class BoxingHelper {
|
||||
* replaced by its unboxed equivalent on top of the stack.
|
||||
*
|
||||
* @param type
|
||||
* the type of the top stack value.
|
||||
* the $Type of the top stack value.
|
||||
*/
|
||||
public void unbox(final Type type){
|
||||
Type t = NUMBER_TYPE;
|
||||
public void unbox(final $Type type){
|
||||
$Type t = NUMBER_$Type;
|
||||
MethodDescriptor sig = null;
|
||||
|
||||
switch(type.getSort()) {
|
||||
case Type.VOID:
|
||||
case $Type.VOID:
|
||||
return;
|
||||
case Type.CHAR:
|
||||
t = CHARACTER_TYPE;
|
||||
case $Type.CHAR:
|
||||
t = CHARACTER_$Type;
|
||||
sig = CHAR_VALUE;
|
||||
break;
|
||||
case Type.BOOLEAN:
|
||||
t = BOOLEAN_TYPE;
|
||||
case $Type.BOOLEAN:
|
||||
t = BOOLEAN_$Type;
|
||||
sig = BOOLEAN_VALUE;
|
||||
break;
|
||||
case Type.DOUBLE:
|
||||
case $Type.DOUBLE:
|
||||
sig = DOUBLE_VALUE;
|
||||
break;
|
||||
case Type.FLOAT:
|
||||
case $Type.FLOAT:
|
||||
sig = FLOAT_VALUE;
|
||||
break;
|
||||
case Type.LONG:
|
||||
case $Type.LONG:
|
||||
sig = LONG_VALUE;
|
||||
break;
|
||||
case Type.INT:
|
||||
case Type.SHORT:
|
||||
case Type.BYTE:
|
||||
case $Type.INT:
|
||||
case $Type.SHORT:
|
||||
case $Type.BYTE:
|
||||
sig = INT_VALUE;
|
||||
}
|
||||
|
||||
@@ -242,13 +242,13 @@ class BoxingHelper {
|
||||
|
||||
/**
|
||||
* Generates the instruction to check that the top stack value is of the
|
||||
* given type.
|
||||
* given $Type.
|
||||
*
|
||||
* @param type a class or interface type.
|
||||
* @param $Type a class or interface $Type.
|
||||
*/
|
||||
public void checkCast(final Type type){
|
||||
if(!type.equals(OBJECT_TYPE)) {
|
||||
typeInsn(Opcodes.CHECKCAST, type);
|
||||
public void checkCast(final $Type $Type){
|
||||
if(!$Type.equals(OBJECT_$Type)) {
|
||||
$TypeInsn($Opcodes.CHECKCAST, $Type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,35 +258,35 @@ class BoxingHelper {
|
||||
* @param owner the class in which the method is defined.
|
||||
* @param method the method to be invoked.
|
||||
*/
|
||||
public void invokeVirtual(final Type owner, final MethodDescriptor method){
|
||||
invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);
|
||||
public void invokeVirtual(final $Type owner, final MethodDescriptor method){
|
||||
invokeInsn($Opcodes.INVOKEVIRTUAL, owner, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an invoke method instruction.
|
||||
*
|
||||
* @param opcode the instruction's opcode.
|
||||
* @param type the class in which the method is defined.
|
||||
* @param $Type the class in which the method is defined.
|
||||
* @param method the method to be invoked.
|
||||
*/
|
||||
private void invokeInsn(final int opcode, final Type type, final MethodDescriptor method){
|
||||
String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName();
|
||||
private void invokeInsn(final int opcode, final $Type $Type, final MethodDescriptor method){
|
||||
String owner = $Type.getSort() == $Type.ARRAY ? $Type.getDescriptor() : $Type.getInternalName();
|
||||
mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a type dependent instruction.
|
||||
* Generates a $Type dependent instruction.
|
||||
*
|
||||
* @param opcode the instruction's opcode.
|
||||
* @param type the instruction's operand.
|
||||
* @param $Type the instruction's operand.
|
||||
*/
|
||||
private void typeInsn(final int opcode, final Type type){
|
||||
private void $TypeInsn(final int opcode, final $Type $Type){
|
||||
String desc;
|
||||
|
||||
if(type.getSort() == Type.ARRAY) {
|
||||
desc = type.getDescriptor();
|
||||
if($Type.getSort() == $Type.ARRAY) {
|
||||
desc = $Type.getDescriptor();
|
||||
} else {
|
||||
desc = type.getInternalName();
|
||||
desc = $Type.getInternalName();
|
||||
}
|
||||
|
||||
mv.visitTypeInsn(opcode, desc);
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ public abstract class CompiledStructureModifier extends StructureModifier<Object
|
||||
@Override
|
||||
public StructureModifier<Object> write(int index, Object value) throws FieldAccessException {
|
||||
if (converter != null)
|
||||
value = converter.getGeneric(getFieldType(index), value);
|
||||
value = converter.getGeneric(value);
|
||||
return writeGenerated(index, value);
|
||||
}
|
||||
|
||||
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.compiler;
|
||||
|
||||
import net.sf.cglib.asm.AnnotationVisitor;
|
||||
import net.sf.cglib.asm.Attribute;
|
||||
import net.sf.cglib.asm.ClassVisitor;
|
||||
import net.sf.cglib.asm.FieldVisitor;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
|
||||
public abstract class EmptyClassVisitor implements ClassVisitor {
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSource(String source, String debug) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
-132
@@ -1,132 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.compiler;
|
||||
|
||||
import net.sf.cglib.asm.AnnotationVisitor;
|
||||
import net.sf.cglib.asm.Attribute;
|
||||
import net.sf.cglib.asm.Label;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
|
||||
public class EmptyMethodVisitor implements MethodVisitor {
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCode() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInsn(int opcode) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIntInsn(int opcode, int operand) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVarInsn(int opcode, int var) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJumpInsn(int opcode, Label label) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLabel(Label label) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object cst) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIincInsn(int var, int increment) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMultiANewArrayInsn(String desc, int dims) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(String name, String desc, String signature, Label start,
|
||||
Label end, int index) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMaxs(int maxStack, int maxLocals) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
+16
-16
@@ -20,7 +20,7 @@ package com.comphenix.protocol.reflect.compiler;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.cglib.asm.Type;
|
||||
import net.sf.cglib.asm.$Type;
|
||||
|
||||
/**
|
||||
* Represents a method.
|
||||
@@ -56,7 +56,7 @@ class MethodDescriptor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Method}.
|
||||
* Creates a new {@link MethodDescriptor}.
|
||||
*
|
||||
* @param name the method's name.
|
||||
* @param desc the method's descriptor.
|
||||
@@ -67,7 +67,7 @@ class MethodDescriptor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Method}.
|
||||
* Creates a new {@link MethodDescriptor}.
|
||||
*
|
||||
* @param name the method's name.
|
||||
* @param returnType the method's return type.
|
||||
@@ -75,14 +75,14 @@ class MethodDescriptor {
|
||||
*/
|
||||
public MethodDescriptor(
|
||||
final String name,
|
||||
final Type returnType,
|
||||
final Type[] argumentTypes)
|
||||
final $Type returnType,
|
||||
final $Type[] argumentTypes)
|
||||
{
|
||||
this(name, Type.getMethodDescriptor(returnType, argumentTypes));
|
||||
this(name, $Type.getMethodDescriptor(returnType, argumentTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Method} corresponding to the given Java method
|
||||
* Returns a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
*
|
||||
* @param method a Java method declaration, without argument names, of the
|
||||
@@ -91,7 +91,7 @@ class MethodDescriptor {
|
||||
* "java.util.List", ...). Classes of the java.lang package can be
|
||||
* specified by their unqualified name; all other classes names must
|
||||
* be fully qualified.
|
||||
* @return a {@link Method} corresponding to the given Java method
|
||||
* @return a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
* @throws IllegalArgumentException if <code>method</code> could not get
|
||||
* parsed.
|
||||
@@ -103,7 +103,7 @@ class MethodDescriptor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Method} corresponding to the given Java method
|
||||
* Returns a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
*
|
||||
* @param method a Java method declaration, without argument names, of the
|
||||
@@ -117,7 +117,7 @@ class MethodDescriptor {
|
||||
* default package, or false if they correspond to java.lang classes.
|
||||
* For instance "Object" means "Object" if this option is true, or
|
||||
* "java.lang.Object" otherwise.
|
||||
* @return a {@link Method} corresponding to the given Java method
|
||||
* @return a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
* @throws IllegalArgumentException if <code>method</code> could not get
|
||||
* parsed.
|
||||
@@ -134,7 +134,7 @@ class MethodDescriptor {
|
||||
}
|
||||
String returnType = method.substring(0, space);
|
||||
String methodName = method.substring(space + 1, start - 1).trim();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(');
|
||||
int p;
|
||||
do {
|
||||
@@ -158,7 +158,7 @@ class MethodDescriptor {
|
||||
return type;
|
||||
}
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int index = 0;
|
||||
while ((index = type.indexOf("[]", index) + 1) > 0) {
|
||||
sb.append('[');
|
||||
@@ -206,8 +206,8 @@ class MethodDescriptor {
|
||||
*
|
||||
* @return the return type of the method described by this object.
|
||||
*/
|
||||
public Type getReturnType() {
|
||||
return Type.getReturnType(desc);
|
||||
public $Type getReturnType() {
|
||||
return $Type.getReturnType(desc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,8 +215,8 @@ class MethodDescriptor {
|
||||
*
|
||||
* @return the argument types of the method described by this object.
|
||||
*/
|
||||
public Type[] getArgumentTypes() {
|
||||
return Type.getArgumentTypes(desc);
|
||||
public $Type[] getArgumentTypes() {
|
||||
return $Type.getArgumentTypes(desc);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
+175
-175
@@ -25,12 +25,12 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.sf.cglib.asm.ClassWriter;
|
||||
import net.sf.cglib.asm.FieldVisitor;
|
||||
import net.sf.cglib.asm.Label;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
import net.sf.cglib.asm.Opcodes;
|
||||
import net.sf.cglib.asm.Type;
|
||||
import net.sf.cglib.asm.$ClassWriter;
|
||||
import net.sf.cglib.asm.$FieldVisitor;
|
||||
import net.sf.cglib.asm.$Label;
|
||||
import net.sf.cglib.asm.$MethodVisitor;
|
||||
import net.sf.cglib.asm.$Opcodes;
|
||||
import net.sf.cglib.asm.$Type;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
@@ -95,22 +95,22 @@ import com.google.common.primitives.Primitives;
|
||||
|
||||
/**
|
||||
* Represents a StructureModifier compiler.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public final class StructureCompiler {
|
||||
public static final ReportType REPORT_TOO_MANY_GENERATED_CLASSES = new ReportType("Generated too many classes (count: %s)");
|
||||
|
||||
|
||||
// Used to store generated classes of different types
|
||||
@SuppressWarnings("rawtypes")
|
||||
static class StructureKey {
|
||||
private Class targetType;
|
||||
private Class fieldType;
|
||||
|
||||
|
||||
public StructureKey(StructureModifier<?> source) {
|
||||
this(source.getTargetType(), source.getFieldType());
|
||||
}
|
||||
|
||||
|
||||
public StructureKey(Class targetType, Class fieldType) {
|
||||
this.targetType = targetType;
|
||||
this.fieldType = fieldType;
|
||||
@@ -120,7 +120,7 @@ public final class StructureCompiler {
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(targetType, fieldType);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof StructureKey) {
|
||||
@@ -131,16 +131,16 @@ public final class StructureCompiler {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Used to load classes
|
||||
private volatile static Method defineMethod;
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Map<StructureKey, Class> compiledCache = new ConcurrentHashMap<StructureKey, Class>();
|
||||
|
||||
|
||||
// The class loader we'll store our classes
|
||||
private ClassLoader loader;
|
||||
|
||||
|
||||
// References to other classes
|
||||
private static String PACKAGE_NAME = "com/comphenix/protocol/reflect/compiler";
|
||||
private static String SUPER_CLASS = "com/comphenix/protocol/reflect/StructureModifier";
|
||||
@@ -156,7 +156,7 @@ public final class StructureCompiler {
|
||||
StructureCompiler(ClassLoader loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lookup the current class loader for any previously generated classes before we attempt to generate something.
|
||||
* @param <TField> Type
|
||||
@@ -194,7 +194,7 @@ public final class StructureCompiler {
|
||||
// We need to compile the class
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compiles the given structure modifier.
|
||||
* <p>
|
||||
@@ -211,15 +211,15 @@ public final class StructureCompiler {
|
||||
if (!isAnyPublic(source.getFields())) {
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
StructureKey key = new StructureKey(source);
|
||||
Class<?> compiledClass = compiledCache.get(key);
|
||||
|
||||
|
||||
if (!compiledCache.containsKey(key)) {
|
||||
compiledClass = generateClass(source);
|
||||
compiledCache.put(key, compiledClass);
|
||||
}
|
||||
|
||||
|
||||
// Next, create an instance of this class
|
||||
try {
|
||||
return (StructureModifier<TField>) compiledClass.getConstructor(
|
||||
@@ -245,7 +245,7 @@ public final class StructureCompiler {
|
||||
throw new IllegalStateException("Cannot happen.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a variable identifier that can uniquely represent the given type.
|
||||
* @param type - a type.
|
||||
@@ -254,7 +254,7 @@ public final class StructureCompiler {
|
||||
private String getSafeTypeName(Class<?> type) {
|
||||
return type.getCanonicalName().replace("[]", "Array").replace(".", "_");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the compiled name of a given structure modifier.
|
||||
* @param source - the structure modifier.
|
||||
@@ -262,29 +262,29 @@ public final class StructureCompiler {
|
||||
*/
|
||||
private String getCompiledName(StructureModifier<?> source) {
|
||||
Class<?> targetType = source.getTargetType();
|
||||
|
||||
|
||||
// Concat class and field type
|
||||
return "CompiledStructure$" +
|
||||
getSafeTypeName(targetType) + "$" +
|
||||
getSafeTypeName(source.getFieldType());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compile a structure modifier.
|
||||
* @param source - structure modifier.
|
||||
* @return The compiled structure modifier.
|
||||
*/
|
||||
private <TField> Class<?> generateClass(StructureModifier<TField> source) {
|
||||
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
|
||||
$ClassWriter cw = new $ClassWriter(0);
|
||||
Class<?> targetType = source.getTargetType();
|
||||
|
||||
|
||||
String className = getCompiledName(source);
|
||||
String targetSignature = Type.getDescriptor(targetType);
|
||||
String targetSignature = $Type.getDescriptor(targetType);
|
||||
String targetName = targetType.getName().replace('.', '/');
|
||||
|
||||
|
||||
// Define class
|
||||
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className,
|
||||
cw.visit($Opcodes.V1_6, $Opcodes.ACC_PUBLIC + $Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className,
|
||||
null, COMPILED_CLASS, null);
|
||||
|
||||
createFields(cw, targetSignature);
|
||||
@@ -292,29 +292,29 @@ public final class StructureCompiler {
|
||||
createReadMethod(cw, className, source.getFields(), targetSignature, targetName);
|
||||
createWriteMethod(cw, className, source.getFields(), targetSignature, targetName);
|
||||
cw.visitEnd();
|
||||
|
||||
|
||||
byte[] data = cw.toByteArray();
|
||||
|
||||
|
||||
// Call the define method
|
||||
try {
|
||||
if (defineMethod == null) {
|
||||
Method defined = ClassLoader.class.getDeclaredMethod("defineClass",
|
||||
new Class<?>[] { String.class, byte[].class, int.class, int.class });
|
||||
|
||||
|
||||
// Awesome. Now, create and return it.
|
||||
defined.setAccessible(true);
|
||||
defineMethod = defined;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class clazz = (Class) defineMethod.invoke(loader, null, data, 0, data.length);
|
||||
|
||||
|
||||
// DEBUG CODE: Print the content of the generated class.
|
||||
//org.objectweb.asm.ClassReader cr = new org.objectweb.asm.ClassReader(data);
|
||||
//cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0);
|
||||
|
||||
|
||||
return clazz;
|
||||
|
||||
|
||||
} catch (SecurityException e) {
|
||||
throw new RuntimeException("Cannot use reflection to dynamically load a class.", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
@@ -327,7 +327,7 @@ public final class StructureCompiler {
|
||||
throw new RuntimeException("Error occured in code generator.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if at least one of the given fields is public.
|
||||
* @param fields - field to test.
|
||||
@@ -340,208 +340,208 @@ public final class StructureCompiler {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private boolean isPublic(Field field) {
|
||||
return Modifier.isPublic(field.getModifiers());
|
||||
}
|
||||
|
||||
|
||||
private boolean isNonFinal(Field field) {
|
||||
return !Modifier.isFinal(field.getModifiers());
|
||||
}
|
||||
|
||||
private void createFields(ClassWriter cw, String targetSignature) {
|
||||
FieldVisitor typedField = cw.visitField(Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null);
|
||||
|
||||
private void createFields($ClassWriter cw, String targetSignature) {
|
||||
$FieldVisitor typedField = cw.visitField($Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null);
|
||||
typedField.visitEnd();
|
||||
}
|
||||
|
||||
private void createWriteMethod(ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
|
||||
|
||||
|
||||
private void createWriteMethod($ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
|
||||
|
||||
String methodDescriptor = "(ILjava/lang/Object;)L" + SUPER_CLASS + ";";
|
||||
String methodSignature = "(ILjava/lang/Object;)L" + SUPER_CLASS + "<Ljava/lang/Object;>;";
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "writeGenerated", methodDescriptor, methodSignature,
|
||||
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PROTECTED, "writeGenerated", methodDescriptor, methodSignature,
|
||||
new String[] { FIELD_EXCEPTION_CLASS });
|
||||
BoxingHelper boxingHelper = new BoxingHelper(mv);
|
||||
|
||||
String generatedClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 3);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
|
||||
// The last label is for the default switch
|
||||
Label[] labels = new Label[fields.size()];
|
||||
Label errorLabel = new Label();
|
||||
Label returnLabel = new Label();
|
||||
|
||||
// Generate labels
|
||||
String generatedClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn($Opcodes.ASTORE, 3);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
|
||||
// The last $Label is for the default switch
|
||||
$Label[] $Labels = new $Label[fields.size()];
|
||||
$Label error$Label = new $Label();
|
||||
$Label return$Label = new $Label();
|
||||
|
||||
// Generate $Labels
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
labels[i] = new Label();
|
||||
$Labels[i] = new $Label();
|
||||
}
|
||||
|
||||
mv.visitTableSwitchInsn(0, labels.length - 1, errorLabel, labels);
|
||||
|
||||
|
||||
mv.visitTableSwitchInsn(0, $Labels.length - 1, error$Label, $Labels);
|
||||
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
|
||||
|
||||
Field field = fields.get(i);
|
||||
Class<?> outputType = field.getType();
|
||||
Class<?> inputType = Primitives.wrap(outputType);
|
||||
String typeDescriptor = Type.getDescriptor(outputType);
|
||||
String typeDescriptor = $Type.getDescriptor(outputType);
|
||||
String inputPath = inputType.getName().replace('.', '/');
|
||||
|
||||
mv.visitLabel(labels[i]);
|
||||
|
||||
|
||||
mv.visitLabel($Labels[i]);
|
||||
|
||||
// Push the compare object
|
||||
if (i == 0)
|
||||
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
|
||||
mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
|
||||
else
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
|
||||
// Only write to public non-final fields
|
||||
if (isPublic(field) && isNonFinal(field)) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 3);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 3);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
|
||||
if (!outputType.isPrimitive())
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, inputPath);
|
||||
mv.visitTypeInsn($Opcodes.CHECKCAST, inputPath);
|
||||
else
|
||||
boxingHelper.unbox(Type.getType(outputType));
|
||||
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor);
|
||||
|
||||
boxingHelper.unbox($Type.getType(outputType));
|
||||
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor);
|
||||
|
||||
} else {
|
||||
// Use reflection. We don't have a choice, unfortunately.
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "writeReflected", "(ILjava/lang/Object;)V");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "writeReflected", "(ILjava/lang/Object;)V");
|
||||
}
|
||||
|
||||
mv.visitJumpInsn(Opcodes.GOTO, returnLabel);
|
||||
|
||||
mv.visitJumpInsn($Opcodes.GOTO, return$Label);
|
||||
}
|
||||
|
||||
mv.visitLabel(errorLabel);
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS);
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
|
||||
mv.visitLabel(error$Label);
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS);
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitLdcInsn("Invalid index ");
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn(Opcodes.ATHROW);
|
||||
|
||||
mv.visitLabel(returnLabel);
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn($Opcodes.ATHROW);
|
||||
|
||||
mv.visitLabel(return$Label);
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitInsn($Opcodes.ARETURN);
|
||||
mv.visitMaxs(5, 4);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void createReadMethod(ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "readGenerated", "(I)Ljava/lang/Object;", null,
|
||||
private void createReadMethod($ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
|
||||
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PROTECTED, "readGenerated", "(I)Ljava/lang/Object;", null,
|
||||
new String[] { "com/comphenix/protocol/reflect/FieldAccessException" });
|
||||
BoxingHelper boxingHelper = new BoxingHelper(mv);
|
||||
|
||||
String generatedClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 2);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
|
||||
// The last label is for the default switch
|
||||
Label[] labels = new Label[fields.size()];
|
||||
Label errorLabel = new Label();
|
||||
|
||||
// Generate labels
|
||||
String generatedClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn($Opcodes.ASTORE, 2);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
|
||||
// The last $Label is for the default switch
|
||||
$Label[] $Labels = new $Label[fields.size()];
|
||||
$Label error$Label = new $Label();
|
||||
|
||||
// Generate $Labels
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
labels[i] = new Label();
|
||||
$Labels[i] = new $Label();
|
||||
}
|
||||
|
||||
mv.visitTableSwitchInsn(0, fields.size() - 1, errorLabel, labels);
|
||||
|
||||
|
||||
mv.visitTableSwitchInsn(0, fields.size() - 1, error$Label, $Labels);
|
||||
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
|
||||
|
||||
Field field = fields.get(i);
|
||||
Class<?> outputType = field.getType();
|
||||
String typeDescriptor = Type.getDescriptor(outputType);
|
||||
|
||||
mv.visitLabel(labels[i]);
|
||||
|
||||
String typeDescriptor = $Type.getDescriptor(outputType);
|
||||
|
||||
mv.visitLabel($Labels[i]);
|
||||
|
||||
// Push the compare object
|
||||
if (i == 0)
|
||||
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
|
||||
mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
|
||||
else
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
|
||||
// Note that byte code cannot access non-public fields
|
||||
if (isPublic(field)) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor);
|
||||
|
||||
boxingHelper.box(Type.getType(outputType));
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor);
|
||||
|
||||
boxingHelper.box($Type.getType(outputType));
|
||||
} else {
|
||||
// We have to use reflection for private and protected fields.
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;");
|
||||
}
|
||||
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
|
||||
mv.visitInsn($Opcodes.ARETURN);
|
||||
}
|
||||
|
||||
mv.visitLabel(errorLabel);
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS);
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitLabel(error$Label);
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS);
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitLdcInsn("Invalid index ");
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn(Opcodes.ATHROW);
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn($Opcodes.ATHROW);
|
||||
mv.visitMaxs(5, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void createConstructor(ClassWriter cw, String className, String targetSignature, String targetName) {
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
|
||||
private void createConstructor($ClassWriter cw, String className, String targetSignature, String targetName) {
|
||||
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PUBLIC, "<init>",
|
||||
"(L" + SUPER_CLASS + ";L" + PACKAGE_NAME + "/StructureCompiler;)V",
|
||||
"(L" + SUPER_CLASS + "<Ljava/lang/Object;>;L" + PACKAGE_NAME + "/StructureCompiler;)V", null);
|
||||
String fullClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, COMPILED_CLASS, "<init>", "()V");
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V");
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;");
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;");
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;");
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, targetName);
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;");
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, COMPILED_CLASS, "<init>", "()V");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;");
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;");
|
||||
mv.visitTypeInsn($Opcodes.CHECKCAST, targetName);
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;");
|
||||
mv.visitInsn($Opcodes.RETURN);
|
||||
mv.visitMaxs(2, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -51,7 +51,7 @@ class CachedPackage {
|
||||
*/
|
||||
public void setPackageClass(String className, Class<?> clazz) {
|
||||
if (clazz != null) {
|
||||
cache.put(className, Optional.<Class<?>> of(clazz));
|
||||
cache.put(className, Optional.of(clazz));
|
||||
} else {
|
||||
cache.remove(className);
|
||||
}
|
||||
@@ -63,32 +63,16 @@ class CachedPackage {
|
||||
* @return Class object.
|
||||
* @throws RuntimeException If we are unable to find the given class.
|
||||
*/
|
||||
public Class<?> getPackageClass(String className) {
|
||||
public Optional<Class<?>> getPackageClass(final String className) {
|
||||
Preconditions.checkNotNull(className, "className cannot be null!");
|
||||
|
||||
// See if we've already looked it up
|
||||
if (cache.containsKey(className)) {
|
||||
Optional<Class<?>> result = cache.get(className);
|
||||
if (!result.isPresent()) {
|
||||
throw new RuntimeException("Cannot find class " + className);
|
||||
return cache.computeIfAbsent(className, x -> {
|
||||
try {
|
||||
return Optional.ofNullable(source.loadClass(combine(packageName, className)));
|
||||
} catch (ClassNotFoundException ex) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return result.get();
|
||||
}
|
||||
|
||||
try {
|
||||
// Try looking it up
|
||||
Class<?> clazz = source.loadClass(combine(packageName, className));
|
||||
if (clazz == null) {
|
||||
throw new IllegalArgumentException("Source " + source + " returned null for " + className);
|
||||
}
|
||||
|
||||
cache.put(className, Optional.<Class<?>> of(clazz));
|
||||
return clazz;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
cache.put(className, Optional.<Class<?>> absent());
|
||||
throw new RuntimeException("Cannot find class " + className, ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ package com.comphenix.protocol.utility;
|
||||
*/
|
||||
|
||||
public final class Constants {
|
||||
public static final String PACKAGE_VERSION = "v1_11_R1";
|
||||
public static final String PACKAGE_VERSION = "v1_13_R2";
|
||||
public static final String NMS = "net.minecraft.server." + PACKAGE_VERSION;
|
||||
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
|
||||
/**
|
||||
* Static methods for accessing Minecraft methods.
|
||||
@@ -43,7 +43,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()) {
|
||||
@@ -96,10 +102,12 @@ public class MinecraftMethods {
|
||||
*/
|
||||
public static Method getNetworkManagerHandleMethod() {
|
||||
if (networkManagerHandle == null) {
|
||||
networkManagerHandle = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass(), true).
|
||||
getMethodByParameters("handle", MinecraftReflection.getPacketClass(), GenericFutureListener[].class);
|
||||
networkManagerHandle = FuzzyReflection
|
||||
.fromClass(MinecraftReflection.getNetworkManagerClass(), true)
|
||||
.getMethodByParameters("handle", MinecraftReflection.getPacketClass());
|
||||
networkManagerHandle.setAccessible(true);
|
||||
}
|
||||
|
||||
return networkManagerHandle;
|
||||
}
|
||||
|
||||
@@ -163,15 +171,16 @@ public class MinecraftMethods {
|
||||
// This object will allow us to detect which methods were called
|
||||
Enhancer enhancer = EnhancerFactory.getInstance().createEnhancer();
|
||||
enhancer.setSuperclass(MinecraftReflection.getPacketDataSerializerClass());
|
||||
enhancer.setCallback(new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
if (method.getName().contains("read"))
|
||||
throw new ReadMethodException();
|
||||
if (method.getName().contains("write"))
|
||||
throw new WriteMethodException();
|
||||
return proxy.invokeSuper(obj, args);
|
||||
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
|
||||
if (method.getName().contains("read")) {
|
||||
throw new ReadMethodException();
|
||||
}
|
||||
|
||||
if (method.getName().contains("write")) {
|
||||
throw new WriteMethodException();
|
||||
}
|
||||
|
||||
return proxy.invokeSuper(obj, args);
|
||||
});
|
||||
|
||||
// Create our proxy object
|
||||
|
||||
@@ -44,6 +44,11 @@ public class MinecraftProtocolVersion {
|
||||
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);
|
||||
map.put(new MinecraftVersion(1, 12, 1), 338);
|
||||
map.put(new MinecraftVersion(1, 12, 2), 340);
|
||||
map.put(new MinecraftVersion(1, 13, 0), 393);
|
||||
map.put(new MinecraftVersion(1, 13, 1), 401);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
+2164
-2123
File diff suppressed because it is too large.
Load diff
@@ -44,6 +44,16 @@ public class MinecraftVersion implements Comparable<MinecraftVersion>, Serializa
|
||||
*/
|
||||
private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-\\.]+)\\s*\\)");
|
||||
|
||||
/**
|
||||
* Version 1.13 - update aquatic.
|
||||
*/
|
||||
public static final MinecraftVersion AQUATIC_UPDATE = new MinecraftVersion("1.13");
|
||||
|
||||
/**
|
||||
* Version 1.12 - the world of color update.
|
||||
*/
|
||||
public static final MinecraftVersion COLOR_UPDATE = new MinecraftVersion("1.12");
|
||||
|
||||
/**
|
||||
* Version 1.11 - the exploration update.
|
||||
*/
|
||||
@@ -346,4 +356,8 @@ public class MinecraftVersion implements Comparable<MinecraftVersion>, Serializa
|
||||
|
||||
return currentVersion;
|
||||
}
|
||||
|
||||
public static boolean atOrAbove(MinecraftVersion version) {
|
||||
return getCurrentVersion().isAtLeast(version);
|
||||
}
|
||||
}
|
||||
@@ -53,24 +53,11 @@ public class Util {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this server is running Spigot. This works by checking
|
||||
* the server version for the String "Spigot"
|
||||
* Whether or not this server is running Spigot or a Spigot fork. This works by checking
|
||||
* the server version for the Strings "Spigot" or "Paper".
|
||||
* @return True if it is, false if not.
|
||||
*/
|
||||
public static boolean isUsingSpigot() {
|
||||
return Bukkit.getServer().getVersion().contains("Spigot");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently running major Java version.
|
||||
* @return The version or -1 if it could not be found
|
||||
*/
|
||||
public static int getJavaVersion() {
|
||||
try {
|
||||
String version = Runtime.class.getPackage().getSpecificationVersion();
|
||||
return (int) (Double.parseDouble(version) * 10 % 10);
|
||||
} catch (Throwable ex) {
|
||||
return -1;
|
||||
}
|
||||
return Bukkit.getServer().getVersion().contains("Spigot") || Bukkit.getServer().getVersion().contains("Paper");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2017 Dan Mulloy
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
/**
|
||||
* Automatically wraps an internal NMS class to a non-versioned, deofbuscated class.
|
||||
* Requirements:
|
||||
* <ul>
|
||||
* <li>The wrapper must be public</li>
|
||||
* <li>If the wrapper is an internal class, it must be static</li>
|
||||
* <li>The wrapper must have one public constructor with no arguments (the default constructor is acceptable)</li>
|
||||
* <li>The wrapper must have the the same number of fields as the NMS class</li>
|
||||
* <li>Each field should correspond, in order, to its NMS counterpart</li>
|
||||
* <li>Non-generic fields must have a converter</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class AutoWrapper<T> implements EquivalentConverter<T> {
|
||||
private Map<Integer, Function<Object, Object>> wrappers = new HashMap<>();
|
||||
private Map<Integer, Function<Object, Object>> unwrappers = new HashMap<>();
|
||||
|
||||
private Class<T> wrapperClass;
|
||||
private Class<?> nmsClass;
|
||||
|
||||
private AutoWrapper(Class<T> wrapperClass, Class<?> nmsClass) {
|
||||
this.wrapperClass = wrapperClass;
|
||||
this.nmsClass = nmsClass;
|
||||
}
|
||||
|
||||
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, Class<?> nmsClass) {
|
||||
return new AutoWrapper<>(wrapperClass, nmsClass);
|
||||
}
|
||||
|
||||
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, String nmsClassName) {
|
||||
return wrap(wrapperClass, MinecraftReflection.getMinecraftClass(nmsClassName));
|
||||
}
|
||||
|
||||
public AutoWrapper<T> field(int index, Function<Object, Object> wrapper, Function<Object, Object> unwrapper) {
|
||||
wrappers.put(index, wrapper);
|
||||
unwrappers.put(index, unwrapper);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AutoWrapper<T> field(int index, EquivalentConverter converter) {
|
||||
return field(index, converter::getSpecific, specific -> converter.getGeneric(specific));
|
||||
}
|
||||
|
||||
public T wrap(Object nmsObject) {
|
||||
T instance;
|
||||
|
||||
try {
|
||||
instance = wrapperClass.newInstance();
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new InvalidWrapperException(wrapperClass.getSimpleName() + " is not accessible!", ex);
|
||||
}
|
||||
|
||||
Field[] wrapperFields = wrapperClass.getDeclaredFields();
|
||||
Field[] nmsFields = Arrays
|
||||
.stream(nmsClass.getDeclaredFields())
|
||||
.filter(field -> !Modifier.isStatic(field.getModifiers()))
|
||||
.toArray(Field[]::new);
|
||||
|
||||
for (int i = 0; i < wrapperFields.length; i++) {
|
||||
try {
|
||||
Field wrapperField = wrapperFields[i];
|
||||
|
||||
Field nmsField = nmsFields[i];
|
||||
if (!nmsField.isAccessible())
|
||||
nmsField.setAccessible(true);
|
||||
|
||||
Object value = nmsField.get(nmsObject);
|
||||
if (wrappers.containsKey(i))
|
||||
value = wrappers.get(i).apply(value);
|
||||
|
||||
wrapperField.set(instance, value);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new InvalidWrapperException("Failed to wrap field", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Object unwrap(Object wrapper) {
|
||||
Object instance;
|
||||
|
||||
try {
|
||||
instance = nmsClass.newInstance();
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new InvalidWrapperException("Failed to construct new " + nmsClass.getSimpleName(), ex);
|
||||
}
|
||||
|
||||
Field[] wrapperFields = wrapperClass.getDeclaredFields();
|
||||
Field[] nmsFields = Arrays
|
||||
.stream(nmsClass.getDeclaredFields())
|
||||
.filter(field -> !Modifier.isStatic(field.getModifiers()))
|
||||
.toArray(Field[]::new);
|
||||
|
||||
for (int i = 0; i < wrapperFields.length; i++) {
|
||||
try {
|
||||
Field wrapperField = wrapperFields[i];
|
||||
|
||||
Field nmsField = nmsFields[i];
|
||||
if (!nmsField.isAccessible())
|
||||
nmsField.setAccessible(true);
|
||||
if (Modifier.isFinal(nmsField.getModifiers()))
|
||||
unsetFinal(nmsField);
|
||||
|
||||
Object value = wrapperField.get(wrapper);
|
||||
if (unwrappers.containsKey(i))
|
||||
value = unwrappers.get(i).apply(value);
|
||||
|
||||
nmsField.set(instance, value);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new InvalidWrapperException("Failed to unwrap field", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private void unsetFinal(Field field) throws ReflectiveOperationException {
|
||||
Field modifiers = Field.class.getDeclaredField("modifiers");
|
||||
modifiers.setAccessible(true);
|
||||
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
}
|
||||
|
||||
// ---- Equivalent conversion
|
||||
|
||||
@Override
|
||||
public T getSpecific(Object generic) {
|
||||
return wrap(generic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(Object specific) {
|
||||
return unwrap(specific);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getSpecificType() {
|
||||
return wrapperClass;
|
||||
}
|
||||
|
||||
public static class InvalidWrapperException extends RuntimeException {
|
||||
private InvalidWrapperException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ public class BlockPosition {
|
||||
public static EquivalentConverter<BlockPosition> getConverter() {
|
||||
return new EquivalentConverter<BlockPosition>() {
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, BlockPosition specific) {
|
||||
public Object getGeneric(BlockPosition specific) {
|
||||
if (blockPositionConstructor == null) {
|
||||
try {
|
||||
blockPositionConstructor = MinecraftReflection.getBlockPositionClass().
|
||||
@@ -187,7 +187,7 @@ public class BlockPosition {
|
||||
public BlockPosition getSpecific(Object generic) {
|
||||
if (MinecraftReflection.isBlockPosition(generic)) {
|
||||
// Use a structure modifier
|
||||
intModifier = new StructureModifier<Object>(generic.getClass(), null, false).withType(int.class);
|
||||
intModifier = new StructureModifier<>(generic.getClass(), null, false).withType(int.class);
|
||||
|
||||
// Damn it all
|
||||
if (intModifier.size() < 3) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -67,7 +67,7 @@ public class ChunkCoordIntPair {
|
||||
public static EquivalentConverter<ChunkCoordIntPair> getConverter() {
|
||||
return new EquivalentConverter<ChunkCoordIntPair>() {
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, ChunkCoordIntPair specific) {
|
||||
public Object getGeneric(ChunkCoordIntPair specific) {
|
||||
if (COORD_CONSTRUCTOR == null) {
|
||||
COORD_CONSTRUCTOR = Accessors.getConstructorAccessor(COORD_PAIR_CLASS, int.class, int.class);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ public class ChunkPosition {
|
||||
public static EquivalentConverter<ChunkPosition> getConverter() {
|
||||
return new EquivalentConverter<ChunkPosition>() {
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, ChunkPosition specific) {
|
||||
public Object getGeneric(ChunkPosition specific) {
|
||||
if (chunkPositionConstructor == null) {
|
||||
try {
|
||||
chunkPositionConstructor = MinecraftReflection.getChunkPositionClass().
|
||||
@@ -177,7 +177,7 @@ public class ChunkPosition {
|
||||
public ChunkPosition getSpecific(Object generic) {
|
||||
if (MinecraftReflection.isChunkPosition(generic)) {
|
||||
// Use a structure modifier
|
||||
intModifier = new StructureModifier<Object>(generic.getClass(), null, false).withType(int.class);
|
||||
intModifier = new StructureModifier<>(generic.getClass(), null, false).withType(int.class);
|
||||
|
||||
// Damn it all
|
||||
if (intModifier.size() < 3) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
public interface ClonableWrapper {
|
||||
Object getHandle();
|
||||
ClonableWrapper deepClone();
|
||||
|
||||
}
|
||||
@@ -20,13 +20,19 @@ import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
/**
|
||||
* Handles component parsing in 1.8
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class ComponentParser {
|
||||
|
||||
|
||||
private static Constructor readerConstructor;
|
||||
private static Method setLenient;
|
||||
private static Method getAdapter;
|
||||
private static Method read;
|
||||
|
||||
private ComponentParser() {
|
||||
}
|
||||
|
||||
@@ -45,13 +51,21 @@ public class ComponentParser {
|
||||
// Should only be needed on 1.8.
|
||||
private static Object deserializeLegacy(Object gson, Class<?> component, StringReader str) {
|
||||
try {
|
||||
Class<?> readerClass = Class.forName("org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonReader");
|
||||
Object reader = readerClass.getConstructor(Reader.class).newInstance(str);
|
||||
Method setLenient = readerClass.getMethod("setLenienent", boolean.class);
|
||||
if(readerConstructor == null){
|
||||
Class<?> readerClass = Class.forName("org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonReader");
|
||||
readerConstructor = readerClass.getDeclaredConstructor(Reader.class);
|
||||
readerConstructor.setAccessible(true);
|
||||
setLenient = readerClass.getDeclaredMethod("setLenient", boolean.class);
|
||||
setLenient.setAccessible(true);
|
||||
getAdapter = gson.getClass().getDeclaredMethod("getAdapter", Class.class);
|
||||
getAdapter.setAccessible(true);
|
||||
Object adapter = getAdapter.invoke(gson, component);
|
||||
read = adapter.getClass().getDeclaredMethod("read", readerClass);
|
||||
read.setAccessible(true);
|
||||
}
|
||||
Object reader = readerConstructor.newInstance(str);
|
||||
setLenient.invoke(reader, true);
|
||||
Method getAdapter = gson.getClass().getMethod("getAdapter", Class.class);
|
||||
Object adapter = getAdapter.invoke(gson, component);
|
||||
Method read = adapter.getClass().getMethod("read", readerClass);
|
||||
return read.invoke(adapter, reader);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to read JSON", ex);
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2017 Dan Mulloy
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
|
||||
/**
|
||||
* Utility class for converters
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class Converters {
|
||||
|
||||
/**
|
||||
* Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them.
|
||||
* @param converter Underlying converter
|
||||
* @param <T> Element type
|
||||
* @return An ignore null converter
|
||||
*/
|
||||
public static <T> EquivalentConverter<T> ignoreNull(final EquivalentConverter<T> converter) {
|
||||
return new EquivalentConverter<T>() {
|
||||
@Override
|
||||
public T getSpecific(Object generic) {
|
||||
return generic != null ? converter.getSpecific(generic) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(T specific) {
|
||||
return specific != null ? converter.getGeneric(specific) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getSpecificType() {
|
||||
return converter.getSpecificType();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a converter that passes generic and specific values through without converting.
|
||||
* @param clazz Element class
|
||||
* @param <T> Element type
|
||||
* @return A passthrough converter
|
||||
*/
|
||||
public static <T> EquivalentConverter<T> passthrough(final Class<T> clazz) {
|
||||
return ignoreNull(new EquivalentConverter<T>() {
|
||||
@Override
|
||||
public T getSpecific(Object generic) {
|
||||
return (T) generic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(T specific) {
|
||||
return specific;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getSpecificType() {
|
||||
return clazz;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8,
|
||||
* converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}).
|
||||
* @param toHandle Function from wrapper to handle (i.e. {@code getHandle()})
|
||||
* @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)})
|
||||
* @param <T> Wrapper type
|
||||
* @return A handle converter
|
||||
*/
|
||||
public static <T> EquivalentConverter<T> handle(final Function<T, Object> toHandle,
|
||||
final Function<Object, T> fromHandle) {
|
||||
return new EquivalentConverter<T>() {
|
||||
@Override
|
||||
public T getSpecific(Object generic) {
|
||||
return fromHandle.apply(generic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(T specific) {
|
||||
return toHandle.apply(specific);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getSpecificType() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,17 @@ import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.GameMode;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import org.bukkit.EntityEffect;
|
||||
import org.bukkit.GameMode;
|
||||
|
||||
/**
|
||||
* Represents a generic enum converter.
|
||||
* @author Kristian
|
||||
@@ -21,26 +23,26 @@ public abstract class EnumWrappers {
|
||||
public enum ClientCommand {
|
||||
PERFORM_RESPAWN,
|
||||
REQUEST_STATS,
|
||||
OPEN_INVENTORY_ACHIEVEMENT;
|
||||
OPEN_INVENTORY_ACHIEVEMENT
|
||||
}
|
||||
|
||||
public enum ChatVisibility {
|
||||
FULL,
|
||||
SYSTEM,
|
||||
HIDDEN;
|
||||
HIDDEN
|
||||
}
|
||||
|
||||
public enum Difficulty {
|
||||
PEACEFUL,
|
||||
EASY,
|
||||
NORMAL,
|
||||
HARD;
|
||||
HARD
|
||||
}
|
||||
|
||||
public enum EntityUseAction {
|
||||
INTERACT,
|
||||
ATTACK,
|
||||
INTERACT_AT;
|
||||
INTERACT_AT
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +112,7 @@ public abstract class EnumWrappers {
|
||||
SUCCESSFULLY_LOADED,
|
||||
DECLINED,
|
||||
FAILED_DOWNLOAD,
|
||||
ACCEPTED;
|
||||
ACCEPTED
|
||||
}
|
||||
|
||||
public enum PlayerInfoAction {
|
||||
@@ -118,15 +120,16 @@ public abstract class EnumWrappers {
|
||||
UPDATE_GAME_MODE,
|
||||
UPDATE_LATENCY,
|
||||
UPDATE_DISPLAY_NAME,
|
||||
REMOVE_PLAYER;
|
||||
REMOVE_PLAYER
|
||||
}
|
||||
|
||||
public enum TitleAction {
|
||||
TITLE,
|
||||
SUBTITLE,
|
||||
ACTIONBAR,
|
||||
TIMES,
|
||||
CLEAR,
|
||||
RESET;
|
||||
RESET
|
||||
}
|
||||
|
||||
public enum WorldBorderAction {
|
||||
@@ -135,13 +138,13 @@ public abstract class EnumWrappers {
|
||||
SET_CENTER,
|
||||
INITIALIZE,
|
||||
SET_WARNING_TIME,
|
||||
SET_WARNING_BLOCKS;
|
||||
SET_WARNING_BLOCKS
|
||||
}
|
||||
|
||||
public enum CombatEventType {
|
||||
ENTER_COMBAT,
|
||||
END_COMBAT,
|
||||
ENTITY_DIED;
|
||||
ENTITY_DIED
|
||||
}
|
||||
|
||||
public enum PlayerDigType {
|
||||
@@ -151,7 +154,7 @@ public abstract class EnumWrappers {
|
||||
DROP_ALL_ITEMS,
|
||||
DROP_ITEM,
|
||||
RELEASE_USE_ITEM,
|
||||
SWAP_HELD_ITEMS;
|
||||
SWAP_HELD_ITEMS
|
||||
}
|
||||
|
||||
public enum PlayerAction {
|
||||
@@ -163,12 +166,12 @@ public abstract class EnumWrappers {
|
||||
START_RIDING_JUMP,
|
||||
STOP_RIDING_JUMP,
|
||||
OPEN_INVENTORY,
|
||||
START_FALL_FLYING;
|
||||
START_FALL_FLYING
|
||||
}
|
||||
|
||||
public enum ScoreboardAction {
|
||||
CHANGE,
|
||||
REMOVE;
|
||||
REMOVE
|
||||
}
|
||||
|
||||
public enum Particle {
|
||||
@@ -218,14 +221,16 @@ public abstract class EnumWrappers {
|
||||
END_ROD("endRod", 43, false),
|
||||
DAMAGE_INDICATOR("damageIndicator", 44, true),
|
||||
SWEEP_ATTACK("sweepAttack", 45, true),
|
||||
FALLING_DUST("fallingdust", 46, 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;
|
||||
|
||||
static {
|
||||
BY_ID = new HashMap<Integer, Particle>();
|
||||
BY_NAME = new HashMap<String, Particle>();
|
||||
BY_ID = new HashMap<>();
|
||||
BY_NAME = new HashMap<>();
|
||||
|
||||
for (Particle particle : values()) {
|
||||
BY_NAME.put(particle.getName().toLowerCase(Locale.ENGLISH), particle);
|
||||
@@ -238,11 +243,11 @@ public abstract class EnumWrappers {
|
||||
private final boolean longDistance;
|
||||
private final int dataLength;
|
||||
|
||||
private Particle(String name, int id, boolean longDistance) {
|
||||
Particle(String name, int id, boolean longDistance) {
|
||||
this(name, id, longDistance, 0);
|
||||
}
|
||||
|
||||
private Particle(String name, int id, boolean longDistance, int dataLength) {
|
||||
Particle(String name, int id, boolean longDistance, int dataLength) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
this.longDistance = longDistance;
|
||||
@@ -295,7 +300,8 @@ public abstract class EnumWrappers {
|
||||
}
|
||||
|
||||
private final String key;
|
||||
private SoundCategory(String key) {
|
||||
|
||||
SoundCategory(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@@ -314,21 +320,31 @@ public abstract class EnumWrappers {
|
||||
FEET,
|
||||
LEGS,
|
||||
CHEST,
|
||||
HEAD;
|
||||
HEAD
|
||||
}
|
||||
|
||||
public enum Hand {
|
||||
MAIN_HAND,
|
||||
OFF_HAND;
|
||||
OFF_HAND
|
||||
}
|
||||
|
||||
public enum Direction {
|
||||
DOWN(),
|
||||
UP(),
|
||||
NORTH(),
|
||||
SOUTH(),
|
||||
WEST(),
|
||||
EAST();
|
||||
DOWN,
|
||||
UP,
|
||||
NORTH,
|
||||
SOUTH,
|
||||
WEST,
|
||||
EAST
|
||||
}
|
||||
|
||||
public enum ChatType {
|
||||
CHAT,
|
||||
SYSTEM,
|
||||
GAME_INFO;
|
||||
|
||||
public byte getId() {
|
||||
return (byte) ordinal();
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> PROTOCOL_CLASS = null;
|
||||
@@ -350,6 +366,7 @@ public abstract class EnumWrappers {
|
||||
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();
|
||||
@@ -386,6 +403,7 @@ public abstract class EnumWrappers {
|
||||
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());
|
||||
@@ -406,6 +424,7 @@ public abstract class EnumWrappers {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -413,6 +432,8 @@ public abstract class EnumWrappers {
|
||||
if (nativeClass != null) {
|
||||
FROM_NATIVE.put(nativeClass, converter);
|
||||
FROM_WRAPPER.put(wrapperClass, converter);
|
||||
} else if (ProtocolLogger.isDebugEnabled()) {
|
||||
// new ClassNotFoundException(wrapperClass.getSimpleName()).printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +447,11 @@ public abstract class EnumWrappers {
|
||||
try {
|
||||
return FuzzyReflection.fromClass(clazz, true).getFieldListByType(Enum.class).get(index).getType();
|
||||
} catch (Throwable ex) {
|
||||
return null; // Unsupported in this version
|
||||
/* if (ProtocolLogger.isDebugEnabled()) {
|
||||
ex.printStackTrace();
|
||||
} */
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,110 +558,129 @@ public abstract class EnumWrappers {
|
||||
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);
|
||||
return new EnumConverter<>(getProtocolClass(), Protocol.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<ClientCommand> getClientCommandConverter() {
|
||||
return new EnumConverter<ClientCommand>(ClientCommand.class);
|
||||
return new EnumConverter<>(getClientCommandClass(), ClientCommand.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<ChatVisibility> getChatVisibilityConverter() {
|
||||
return new EnumConverter<ChatVisibility>(ChatVisibility.class);
|
||||
return new EnumConverter<>(getChatVisibilityClass(), ChatVisibility.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<Difficulty> getDifficultyConverter() {
|
||||
return new EnumConverter<Difficulty>(Difficulty.class);
|
||||
return new EnumConverter<>(getDifficultyClass(), Difficulty.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<EntityUseAction> getEntityUseActionConverter() {
|
||||
return new EnumConverter<EntityUseAction>(EntityUseAction.class);
|
||||
return new EnumConverter<>(getEntityUseActionClass(), EntityUseAction.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<NativeGameMode> getGameModeConverter() {
|
||||
return new EnumConverter<NativeGameMode>(NativeGameMode.class);
|
||||
return new EnumConverter<>(getGameModeClass(), NativeGameMode.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<ResourcePackStatus> getResourcePackStatusConverter() {
|
||||
return new EnumConverter<ResourcePackStatus>(ResourcePackStatus.class);
|
||||
return new EnumConverter<>(getResourcePackStatusClass(), ResourcePackStatus.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<PlayerInfoAction> getPlayerInfoActionConverter() {
|
||||
return new EnumConverter<PlayerInfoAction>(PlayerInfoAction.class);
|
||||
return new EnumConverter<>(getPlayerInfoActionClass(), PlayerInfoAction.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<TitleAction> getTitleActionConverter() {
|
||||
return new EnumConverter<TitleAction>(TitleAction.class);
|
||||
return new EnumConverter<>(getTitleActionClass(), TitleAction.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<WorldBorderAction> getWorldBorderActionConverter() {
|
||||
return new EnumConverter<WorldBorderAction>(WorldBorderAction.class);
|
||||
return new EnumConverter<>(getWorldBorderActionClass(), WorldBorderAction.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<CombatEventType> getCombatEventTypeConverter() {
|
||||
return new EnumConverter<CombatEventType>(CombatEventType.class);
|
||||
return new EnumConverter<>(getCombatEventTypeClass(), CombatEventType.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<PlayerDigType> getPlayerDiggingActionConverter() {
|
||||
return new EnumConverter<PlayerDigType>(PlayerDigType.class);
|
||||
return new EnumConverter<>(getPlayerDigTypeClass(), PlayerDigType.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<PlayerAction> getEntityActionConverter() {
|
||||
return new EnumConverter<PlayerAction>(PlayerAction.class);
|
||||
return new EnumConverter<>(getPlayerActionClass(), PlayerAction.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<ScoreboardAction> getUpdateScoreActionConverter() {
|
||||
return new EnumConverter<ScoreboardAction>(ScoreboardAction.class);
|
||||
return new EnumConverter<>(getScoreboardActionClass(), ScoreboardAction.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<Particle> getParticleConverter() {
|
||||
return new EnumConverter<Particle>(Particle.class);
|
||||
return new EnumConverter<>(getParticleClass(), Particle.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<SoundCategory> getSoundCategoryConverter() {
|
||||
return new EnumConverter<SoundCategory>(SoundCategory.class);
|
||||
return new EnumConverter<>(getSoundCategoryClass(), SoundCategory.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<ItemSlot> getItemSlotConverter() {
|
||||
return new EnumConverter<ItemSlot>(ItemSlot.class);
|
||||
return new EnumConverter<>(getItemSlotClass(), ItemSlot.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<Hand> getHandConverter() {
|
||||
return new EnumConverter<Hand>(Hand.class);
|
||||
return new EnumConverter<>(getHandClass(), Hand.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<Direction> getDirectionConverter() {
|
||||
return new EnumConverter<Direction>(Direction.class);
|
||||
return new EnumConverter<>(getDirectionClass(), Direction.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<ChatType> getChatTypeConverter() {
|
||||
return new EnumConverter<>(getChatTypeClass(), ChatType.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a generic enum converter for use with StructureModifiers.
|
||||
* @param enumClass - Enum class
|
||||
* @param genericClass - Generic nms enum class
|
||||
* @param specificType - Specific enum class
|
||||
* @return A generic enum converter
|
||||
*/
|
||||
public static <T extends Enum<T>> EquivalentConverter<T> getGenericConverter(Class<T> enumClass) {
|
||||
return new EnumConverter<T>(enumClass);
|
||||
public static <T extends Enum<T>> EquivalentConverter<T> getGenericConverter(Class<?> genericClass, Class<T> specificType) {
|
||||
return new EnumConverter<>(genericClass, specificType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Replaced with {@link #getGenericConverter(Class, Class)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static <T extends Enum<T>> EquivalentConverter<T> getGenericConverter(Class<T> specificType) {
|
||||
return new EnumConverter<>(null, specificType);
|
||||
}
|
||||
|
||||
// The common enum converter
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public static class EnumConverter<T extends Enum<T>> implements EquivalentConverter<T> {
|
||||
private Class<?> genericType;
|
||||
private Class<T> specificType;
|
||||
|
||||
public EnumConverter(Class<T> specificType) {
|
||||
public EnumConverter(Class<?> genericType, Class<T> specificType) {
|
||||
this.genericType = genericType;
|
||||
this.specificType = specificType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getSpecific(Object generic) {
|
||||
// We know its an enum already!
|
||||
return Enum.valueOf(specificType, ((Enum) generic).name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, T specific) {
|
||||
public Object getGeneric(T specific) {
|
||||
return Enum.valueOf((Class) genericType, specific.name());
|
||||
}
|
||||
|
||||
@@ -644,5 +688,9 @@ public abstract class EnumWrappers {
|
||||
public Class<T> getSpecificType() {
|
||||
return specificType;
|
||||
}
|
||||
|
||||
void setGenericType(Class<?> genericType) {
|
||||
this.genericType = genericType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public class MinecraftKey {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, MinecraftKey specific) {
|
||||
public Object getGeneric(MinecraftKey specific) {
|
||||
if (constructor == null) {
|
||||
try {
|
||||
constructor = MinecraftReflection.getMinecraftKeyClass().getConstructor(String.class, String.class);
|
||||
|
||||
@@ -190,7 +190,7 @@ public class MultiBlockChangeInfo {
|
||||
|
||||
@Override
|
||||
public MultiBlockChangeInfo getSpecific(Object generic) {
|
||||
StructureModifier<Object> modifier = new StructureModifier<Object>(generic.getClass(), null, false).withTarget(generic);
|
||||
StructureModifier<Object> modifier = new StructureModifier<>(generic.getClass(), null, false).withTarget(generic);
|
||||
|
||||
StructureModifier<Short> shorts = modifier.withType(short.class);
|
||||
short location = shorts.read(0);
|
||||
@@ -203,7 +203,7 @@ public class MultiBlockChangeInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, MultiBlockChangeInfo specific) {
|
||||
public Object getGeneric(MultiBlockChangeInfo specific) {
|
||||
try {
|
||||
if (constructor == null) {
|
||||
constructor = nmsClass.getConstructor(
|
||||
@@ -216,7 +216,7 @@ public class MultiBlockChangeInfo {
|
||||
return constructor.newInstance(
|
||||
null,
|
||||
specific.location,
|
||||
BukkitConverters.getWrappedBlockDataConverter().getGeneric(MinecraftReflection.getIBlockDataClass(), specific.data)
|
||||
BukkitConverters.getWrappedBlockDataConverter().getGeneric(specific.data)
|
||||
);
|
||||
} catch (Throwable ex) {
|
||||
throw new RuntimeException("Failed to construct MultiBlockChangeInfo instance.", ex);
|
||||
@@ -248,12 +248,12 @@ public class MultiBlockChangeInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, MultiBlockChangeInfo[] specific) {
|
||||
public Object getGeneric(MultiBlockChangeInfo[] specific) {
|
||||
Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length);
|
||||
|
||||
// Wrap every item
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = converter.getGeneric(nmsClass, specific[i]);
|
||||
result[i] = converter.getGeneric(specific[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -91,7 +91,7 @@ public class PlayerInfoData {
|
||||
public static EquivalentConverter<PlayerInfoData> getConverter() {
|
||||
return new EquivalentConverter<PlayerInfoData>() {
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, PlayerInfoData specific) {
|
||||
public Object getGeneric(PlayerInfoData specific) {
|
||||
if (constructor == null) {
|
||||
try {
|
||||
// public PlayerInfoData(Packet, GameProfile, int, GameMode, ChatComponent)
|
||||
@@ -116,7 +116,7 @@ public class PlayerInfoData {
|
||||
null,
|
||||
specific.profile.handle,
|
||||
specific.latency,
|
||||
EnumWrappers.getGameModeConverter().getGeneric(EnumWrappers.getGameModeClass(), specific.gameMode),
|
||||
EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode),
|
||||
specific.displayName != null ? specific.displayName.handle : null
|
||||
);
|
||||
return result;
|
||||
@@ -128,7 +128,7 @@ public class PlayerInfoData {
|
||||
@Override
|
||||
public PlayerInfoData getSpecific(Object generic) {
|
||||
if (MinecraftReflection.isPlayerInfoData(generic)) {
|
||||
StructureModifier<Object> modifier = new StructureModifier<Object>(generic.getClass(), null, false)
|
||||
StructureModifier<Object> modifier = new StructureModifier<>(generic.getClass(), null, false)
|
||||
.withTarget(generic);
|
||||
|
||||
StructureModifier<WrappedGameProfile> gameProfiles = modifier.withType(
|
||||
|
||||
@@ -91,15 +91,14 @@ public class Vector3F {
|
||||
}
|
||||
|
||||
public static EquivalentConverter<Vector3F> getConverter() {
|
||||
return new IgnoreNullConverter<Vector3F>() {
|
||||
|
||||
return Converters.ignoreNull(new EquivalentConverter<Vector3F>() {
|
||||
@Override
|
||||
public Class<Vector3F> getSpecificType() {
|
||||
return Vector3F.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getGenericValue(Class<?> genericType, Vector3F specific) {
|
||||
public Object getGeneric(Vector3F specific) {
|
||||
if (constructor == null) {
|
||||
try {
|
||||
constructor = clazz.getConstructor(float.class, float.class, float.class);
|
||||
@@ -107,7 +106,7 @@ public class Vector3F {
|
||||
throw new RuntimeException("Failed to find constructor for Vector3f", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
return constructor.newInstance(specific.x, specific.y, specific.z);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
@@ -116,7 +115,7 @@ public class Vector3F {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vector3F getSpecificValue(Object generic) {
|
||||
public Vector3F getSpecific(Object generic) {
|
||||
StructureModifier<Float> modifier = new StructureModifier<Float>(generic.getClass())
|
||||
.withTarget(generic).withType(float.class);
|
||||
float x = modifier.read(0);
|
||||
@@ -124,6 +123,6 @@ public class Vector3F {
|
||||
float z = modifier.read(2);
|
||||
return new Vector3F(x, y, z);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+14
-5
@@ -2,7 +2,7 @@ package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.UUID;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
@@ -91,7 +91,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
|
||||
|
||||
// Cached values
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
private final Supplier<String> name;
|
||||
private final Operation operation;
|
||||
private final double amount;
|
||||
|
||||
@@ -109,7 +109,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
|
||||
|
||||
// Use the supplied values instead of reading from the NMS instance
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
this.name = () -> name;
|
||||
this.amount = amount;
|
||||
this.operation = operation;
|
||||
}
|
||||
@@ -118,6 +118,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
|
||||
* Construct an attribute modifier wrapper around a given NMS instance.
|
||||
* @param handle - the NMS instance.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected WrappedAttributeModifier(@Nonnull Object handle) {
|
||||
// Update handle and modifier
|
||||
super(MinecraftReflection.getAttributeModifierClass());
|
||||
@@ -126,7 +127,15 @@ public class WrappedAttributeModifier extends AbstractWrapper {
|
||||
|
||||
// Load final values, caching them
|
||||
this.uuid = (UUID) modifier.withType(UUID.class).read(0);
|
||||
this.name = (String) modifier.withType(String.class).read(0);
|
||||
|
||||
StructureModifier<String> stringMod = modifier.withType(String.class);
|
||||
if (stringMod.size() == 0) {
|
||||
Supplier<String> supplier = (Supplier<String>) modifier.withType(Supplier.class).read(0);
|
||||
this.name = supplier;
|
||||
} else {
|
||||
this.name = () -> stringMod.read(0);
|
||||
}
|
||||
|
||||
this.amount = (Double) modifier.withType(double.class).read(0);
|
||||
this.operation = Operation.fromId((Integer) modifier.withType(int.class).read(0));
|
||||
}
|
||||
@@ -213,7 +222,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
|
||||
* @return The attribute key.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
return name.get();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,13 +18,15 @@ package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.bukkit.Material;
|
||||
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
/**
|
||||
* Represents a wrapper around IBlockData.
|
||||
@@ -32,39 +34,217 @@ import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
* @author dmulloy2
|
||||
*/
|
||||
|
||||
public class WrappedBlockData extends AbstractWrapper {
|
||||
public abstract class WrappedBlockData extends AbstractWrapper implements ClonableWrapper {
|
||||
private static final Class<?> MAGIC_NUMBERS = MinecraftReflection.getCraftBukkitClass("util.CraftMagicNumbers");
|
||||
private static final Class<?> IBLOCK_DATA = MinecraftReflection.getIBlockDataClass();
|
||||
private static final Class<?> BLOCK = MinecraftReflection.getBlockClass();
|
||||
|
||||
private static MethodAccessor FROM_LEGACY_DATA = null;
|
||||
private static MethodAccessor TO_LEGACY_DATA = null;
|
||||
private static MethodAccessor GET_NMS_BLOCK = null;
|
||||
private static MethodAccessor GET_BLOCK = null;
|
||||
private static class NewBlockData extends WrappedBlockData {
|
||||
private static MethodAccessor MATERIAL_FROM_BLOCK;
|
||||
private static MethodAccessor TO_LEGACY_DATA;
|
||||
private static MethodAccessor GET_BLOCK;
|
||||
private static MethodAccessor BLOCK_FROM_MATERIAL;
|
||||
private static MethodAccessor GET_BLOCK_DATA;
|
||||
private static MethodAccessor FROM_LEGACY_DATA;
|
||||
private static MethodAccessor GET_HANDLE;
|
||||
|
||||
static {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(BLOCK);
|
||||
FuzzyMethodContract contract = FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.parameterExactArray(int.class)
|
||||
.returnTypeExact(IBLOCK_DATA)
|
||||
.build();
|
||||
FROM_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
static {
|
||||
if (MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MAGIC_NUMBERS);
|
||||
FuzzyMethodContract contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.returnTypeExact(Material.class)
|
||||
.parameterExactArray(BLOCK)
|
||||
.build();
|
||||
MATERIAL_FROM_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
|
||||
contract = FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.parameterExactArray(IBLOCK_DATA)
|
||||
.returnTypeExact(int.class)
|
||||
.build();
|
||||
TO_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "toLegacyData"));
|
||||
contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.parameterExactArray(Material.class)
|
||||
.returnTypeExact(BLOCK)
|
||||
.build();
|
||||
BLOCK_FROM_MATERIAL = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
|
||||
fuzzy = FuzzyReflection.fromClass(MAGIC_NUMBERS);
|
||||
GET_NMS_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
|
||||
new Class<?>[] { Material.class }));
|
||||
contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.parameterExactArray(IBLOCK_DATA)
|
||||
.returnTypeExact(byte.class)
|
||||
.build();
|
||||
TO_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
|
||||
fuzzy = FuzzyReflection.fromClass(IBLOCK_DATA);
|
||||
GET_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
|
||||
new Class<?>[0]));
|
||||
contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.parameterExactArray(Material.class, byte.class)
|
||||
.returnTypeExact(IBLOCK_DATA)
|
||||
.build();
|
||||
FROM_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
|
||||
fuzzy = FuzzyReflection.fromClass(IBLOCK_DATA);
|
||||
contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnTypeExact(BLOCK)
|
||||
.parameterCount(0)
|
||||
.build();
|
||||
GET_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
|
||||
fuzzy = FuzzyReflection.fromClass(BLOCK);
|
||||
contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.parameterCount(0)
|
||||
.returnTypeExact(IBLOCK_DATA)
|
||||
.build();
|
||||
GET_BLOCK_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
|
||||
fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData"));
|
||||
contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.parameterCount(0)
|
||||
.returnTypeExact(IBLOCK_DATA)
|
||||
.build();
|
||||
GET_HANDLE = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
}
|
||||
}
|
||||
|
||||
private NewBlockData(Object handle) {
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Material getType() {
|
||||
Object block = GET_BLOCK.invoke(handle);
|
||||
return (Material) MATERIAL_FROM_BLOCK.invoke(null, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getData() {
|
||||
return ((Number) TO_LEGACY_DATA.invoke(null, handle)).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(Material material) {
|
||||
Object block = BLOCK_FROM_MATERIAL.invoke(null, material);
|
||||
setHandle(GET_BLOCK_DATA.invoke(block));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(int data) {
|
||||
setTypeAndData(getType(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTypeAndData(Material material, int data) {
|
||||
setHandle(TO_LEGACY_DATA.invoke(null, material, (byte) data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedBlockData deepClone() {
|
||||
return NewBlockData.createNewData(getType(), getData());
|
||||
}
|
||||
|
||||
private static WrappedBlockData createNewData(Material material) {
|
||||
Object block = BLOCK_FROM_MATERIAL.invoke(null, material);
|
||||
return new NewBlockData(GET_BLOCK_DATA.invoke(block));
|
||||
}
|
||||
|
||||
private static WrappedBlockData createNewData(Material material, int data) {
|
||||
return new NewBlockData(FROM_LEGACY_DATA.invoke(null, material, (byte) data));
|
||||
}
|
||||
|
||||
private static WrappedBlockData createNewData(BlockData data) {
|
||||
return new NewBlockData(GET_HANDLE.invoke(data));
|
||||
}
|
||||
}
|
||||
|
||||
private static class OldBlockData extends WrappedBlockData {
|
||||
private static MethodAccessor FROM_LEGACY_DATA;
|
||||
private static MethodAccessor TO_LEGACY_DATA;
|
||||
private static MethodAccessor GET_NMS_BLOCK;
|
||||
private static MethodAccessor GET_BLOCK;
|
||||
|
||||
static {
|
||||
if (!MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(BLOCK);
|
||||
FuzzyMethodContract contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.parameterExactArray(int.class)
|
||||
.returnTypeExact(IBLOCK_DATA)
|
||||
.build();
|
||||
FROM_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
|
||||
contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.parameterExactArray(IBLOCK_DATA)
|
||||
.returnTypeExact(int.class)
|
||||
.build();
|
||||
TO_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "toLegacyData"));
|
||||
|
||||
fuzzy = FuzzyReflection.fromClass(MAGIC_NUMBERS);
|
||||
GET_NMS_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
|
||||
new Class<?>[]{Material.class}));
|
||||
|
||||
fuzzy = FuzzyReflection.fromClass(IBLOCK_DATA);
|
||||
GET_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
|
||||
new Class<?>[0]));
|
||||
}
|
||||
}
|
||||
|
||||
private OldBlockData(Object handle) {
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Material getType() {
|
||||
Object block = GET_BLOCK.invoke(handle);
|
||||
return BukkitConverters.getBlockConverter().getSpecific(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getData() {
|
||||
Object block = GET_BLOCK.invoke(handle);
|
||||
return (Integer) TO_LEGACY_DATA.invoke(block, handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(Material type) {
|
||||
setTypeAndData(type, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(int data) {
|
||||
setTypeAndData(getType(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTypeAndData(Material type, int data) {
|
||||
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
|
||||
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
|
||||
setHandle(blockData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedBlockData deepClone() {
|
||||
return WrappedBlockData.createData(getType(), getData());
|
||||
}
|
||||
|
||||
private static WrappedBlockData createOldData(Material type) {
|
||||
Object blockData = GET_BLOCK.invoke(null, type);
|
||||
return new OldBlockData(blockData);
|
||||
}
|
||||
|
||||
private static WrappedBlockData createOldData(Material type, int data) {
|
||||
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
|
||||
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
|
||||
return new OldBlockData(blockData);
|
||||
}
|
||||
}
|
||||
|
||||
public WrappedBlockData(Object handle) {
|
||||
@@ -73,60 +253,43 @@ public class WrappedBlockData extends AbstractWrapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of this BlockData.
|
||||
* @return The type of this BlockData.
|
||||
* Gets this BlockData's Bukkit material
|
||||
* @return The Bukkit material
|
||||
*/
|
||||
public Material getType() {
|
||||
Object block = GET_BLOCK.invoke(handle);
|
||||
return BukkitConverters.getBlockConverter().getSpecific(block);
|
||||
}
|
||||
public abstract Material getType();
|
||||
|
||||
/**
|
||||
* Retrieves the type id of this BlockData.
|
||||
* @return The type id of this BlockData.
|
||||
* @deprecated ID's are deprecated
|
||||
* Gets this BlockData's legacy data
|
||||
* @return The legacy data
|
||||
* @deprecated By the flattening in 1.13. Fine on lower versions.
|
||||
*/
|
||||
@Deprecated
|
||||
public int getTypeId() {
|
||||
Object block = GET_BLOCK.invoke(handle);
|
||||
return BukkitConverters.getBlockIDConverter().getSpecific(block);
|
||||
}
|
||||
public abstract int getData();
|
||||
|
||||
/**
|
||||
* Retrieves the data of this BlockData.
|
||||
* @return The data of this BlockData.
|
||||
* Sets this BlockData's type
|
||||
* @param material Bukkit material
|
||||
*/
|
||||
public int getData() {
|
||||
Object block = GET_BLOCK.invoke(handle);
|
||||
return (Integer) TO_LEGACY_DATA.invoke(block, handle);
|
||||
}
|
||||
public abstract void setType(Material material);
|
||||
|
||||
/**
|
||||
* Sets the type of this BlockData.
|
||||
* @param type New type
|
||||
* Sets this BlockData's legacy data
|
||||
* @param data The new legacy data
|
||||
* @deprecated By the flattening in 1.13. Fine on lower versions.
|
||||
*/
|
||||
public void setType(Material type) {
|
||||
setTypeAndData(type, 0);
|
||||
}
|
||||
@Deprecated
|
||||
public abstract void setData(int data);
|
||||
|
||||
/**
|
||||
* Sets the data of this BlockData.
|
||||
* @param data New data
|
||||
* Sets this BlockData's type and legacy data
|
||||
* @param material The new Bukkit material
|
||||
* @param data The new legacy data
|
||||
* @deprecated By the flattening in 1.13. Fine on lower versions.
|
||||
*/
|
||||
public void setData(int data) {
|
||||
setTypeAndData(getType(), data);
|
||||
}
|
||||
@Deprecated
|
||||
public abstract void setTypeAndData(Material material, int data);
|
||||
|
||||
/**
|
||||
* Sets the type and data of this BlockData.
|
||||
* @param type New type
|
||||
* @param data New data
|
||||
*/
|
||||
public void setTypeAndData(Material type, int data) {
|
||||
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
|
||||
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
|
||||
setHandle(blockData);
|
||||
}
|
||||
public abstract WrappedBlockData deepClone();
|
||||
|
||||
/**
|
||||
* Creates a new BlockData instance with the given type and no data.
|
||||
@@ -134,7 +297,8 @@ public class WrappedBlockData extends AbstractWrapper {
|
||||
* @return New BlockData
|
||||
*/
|
||||
public static WrappedBlockData createData(Material type) {
|
||||
return createData(type, 0);
|
||||
return MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE) ? NewBlockData.createNewData(type)
|
||||
: OldBlockData.createOldData(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,19 +306,26 @@ public class WrappedBlockData extends AbstractWrapper {
|
||||
* @param type Block type
|
||||
* @param data Block data
|
||||
* @return New BlockData
|
||||
* @deprecated The flattening
|
||||
*/
|
||||
@Deprecated
|
||||
public static WrappedBlockData createData(Material type, int data) {
|
||||
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
|
||||
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
|
||||
return new WrappedBlockData(blockData);
|
||||
return MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE) ? NewBlockData.createNewData(type, data)
|
||||
: OldBlockData.createOldData(type, data);
|
||||
}
|
||||
|
||||
public static WrappedBlockData fromHandle(Object handle) {
|
||||
return MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE) ? new NewBlockData(handle)
|
||||
: new OldBlockData(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a deep copy of the current wrapper object.
|
||||
* @return The cloned object.
|
||||
* Creates a new Wrapped Block Data instance from a given Spigot Block Data
|
||||
* @param data Spigot block data
|
||||
* @return The new Wrapped Block Data
|
||||
*/
|
||||
public WrappedBlockData deepClone() {
|
||||
return WrappedBlockData.createData(getType(), getData());
|
||||
public static WrappedBlockData createData(BlockData data) {
|
||||
return NewBlockData.createNewData(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,11 +344,14 @@ public class WrappedBlockData extends AbstractWrapper {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
|
||||
if (o instanceof WrappedBlockData) {
|
||||
WrappedBlockData that = (WrappedBlockData) o;
|
||||
return this.getType() == that.getType() && getData() == that.getData();
|
||||
return this.handle.equals(that.handle)
|
||||
|| (this.getType() == that.getType() && this.getData() == that.getData());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2016 dmulloy2
|
||||
* Copyright (C) 2018 dmulloy2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
@@ -16,21 +16,8 @@
|
||||
*/
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
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 java.util.UUID;
|
||||
|
||||
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;
|
||||
@@ -47,12 +34,15 @@ 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.8 thru 1.10
|
||||
*
|
||||
* Represents a DataWatcher
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject> {
|
||||
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject>, ClonableWrapper {
|
||||
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherClass();
|
||||
|
||||
private static MethodAccessor GETTER = null;
|
||||
@@ -64,7 +54,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
private static FieldAccessor MAP_FIELD = null;
|
||||
|
||||
private static ConstructorAccessor constructor = null;
|
||||
private static ConstructorAccessor lightningConstructor = null;
|
||||
private static ConstructorAccessor eggConstructor = null;
|
||||
|
||||
private static Object fakeEntity = null;
|
||||
|
||||
@@ -83,7 +73,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new DataWatcher using a fake lightning entity. The
|
||||
* Constructs a new DataWatcher using a fake egg entity. The
|
||||
* resulting DataWatcher will not have any keys or values and new ones will
|
||||
* have to be added using watcher objects.
|
||||
*/
|
||||
@@ -103,7 +93,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new DataWatcher using a fake lightning entity and a given
|
||||
* Constructs a new DataWatcher using a fake egg entity and a given
|
||||
* list of watchable objects.
|
||||
*
|
||||
* @param objects The list of objects
|
||||
@@ -135,13 +125,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
return fakeEntity;
|
||||
}
|
||||
|
||||
// We can create a fake lightning strike without it affecting anything
|
||||
if (lightningConstructor == null) {
|
||||
lightningConstructor = Accessors.getConstructorAccessor(MinecraftReflection.getMinecraftClass("EntityLightning"),
|
||||
MinecraftReflection.getNmsWorldClass(), double.class, double.class, double.class, boolean.class);
|
||||
// We can create a fake egg without it affecting anything
|
||||
// Mojang added difficulty to lightning strikes, so this'll have to do
|
||||
if (eggConstructor == null) {
|
||||
eggConstructor = Accessors.getConstructorAccessor(MinecraftReflection.getMinecraftClass("EntityEgg"),
|
||||
MinecraftReflection.getNmsWorldClass(), double.class, double.class, double.class);
|
||||
}
|
||||
|
||||
return fakeEntity = lightningConstructor.invoke(null, 0, 0, 0, true);
|
||||
return fakeEntity = eggConstructor.invoke(null, 0, 0, 0);
|
||||
}
|
||||
|
||||
// ---- Collection Methods
|
||||
@@ -149,15 +140,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<Integer, Object> getMap() {
|
||||
if (MAP_FIELD == null) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
|
||||
MAP_FIELD = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.typeDerivedOf(Map.class)
|
||||
.build()));
|
||||
}
|
||||
|
||||
if (MAP_FIELD == null) {
|
||||
throw new FieldAccessException("Could not find index <-> Item map.");
|
||||
try {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
|
||||
MAP_FIELD = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract
|
||||
.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.typeDerivedOf(Map.class)
|
||||
.build()));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new FieldAccessException("Failed to find watchable object map");
|
||||
}
|
||||
}
|
||||
|
||||
return (Map<Integer, Object>) MAP_FIELD.get(handle);
|
||||
@@ -380,7 +372,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
try {
|
||||
Object value = GETTER.invoke(handle, object.getHandle());
|
||||
return WrappedWatchableObject.getWrapped(value);
|
||||
} catch (RuntimeException ex) {
|
||||
} catch (Exception ex) {
|
||||
// Nothing exists at this index
|
||||
return null;
|
||||
}
|
||||
@@ -419,7 +411,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
* 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
|
||||
*
|
||||
@@ -463,7 +455,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
* @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);
|
||||
@@ -686,7 +678,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
*/
|
||||
public WrappedDataWatcherObject(Object handle) {
|
||||
this.handle = handle;
|
||||
this.modifier = new StructureModifier<Object>(HANDLE_TYPE).withTarget(handle);
|
||||
this.modifier = new StructureModifier<>(HANDLE_TYPE).withTarget(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -701,18 +693,31 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
static WrappedDataWatcherObject fromIndex(int index) {
|
||||
if (MinecraftReflection.watcherObjectExists()) {
|
||||
return new WrappedDataWatcherObject(index, null);
|
||||
return new WrappedDataWatcherObject(newHandle(index));
|
||||
} else {
|
||||
return new DummyWatcherObject(index);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object newHandle(int index, Serializer serializer) {
|
||||
private static Object newHandle(int index) {
|
||||
Validate.isTrue(index >= 0, "index cannot be negative!");
|
||||
|
||||
if (constructor == null) {
|
||||
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
|
||||
}
|
||||
|
||||
Object handle = serializer != null ? serializer.getHandle() : null;
|
||||
return constructor.invoke(index, null);
|
||||
}
|
||||
|
||||
private static Object newHandle(int index, Serializer serializer) {
|
||||
Validate.isTrue(index >= 0, "index cannot be negative!");
|
||||
Validate.notNull(serializer, "serializer cannot be null!");
|
||||
|
||||
if (constructor == null) {
|
||||
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
|
||||
}
|
||||
|
||||
Object handle = serializer.getHandle();
|
||||
return constructor.invoke(index, handle);
|
||||
}
|
||||
|
||||
@@ -893,7 +898,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
* <li>Float</li>
|
||||
* <li>String</li>
|
||||
* <li>IChatBaseComponent</li>
|
||||
* <li>Optional<ItemStack></li>
|
||||
* <li>ItemStack</li>
|
||||
* <li>Optional<IBlockData></li>
|
||||
* <li>Boolean</li>
|
||||
* <li>Vector3f</li>
|
||||
@@ -901,6 +906,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
* <li>Optional<BlockPosition></li>
|
||||
* <li>EnumDirection</li>
|
||||
* <li>Optional<UUID></li>
|
||||
* <li>NBTTagCompound</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author dmulloy2
|
||||
@@ -922,7 +928,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
* @return The serializer, or null if none exists
|
||||
*/
|
||||
public static Serializer get(Class<?> clazz) {
|
||||
Validate.notNull("Class cannot be null!");
|
||||
Validate.notNull(clazz,"Class cannot be null!");
|
||||
initialize();
|
||||
|
||||
for (Serializer serializer : REGISTRY) {
|
||||
@@ -931,7 +937,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
throw new IllegalArgumentException("No serializer found for " + clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -948,6 +954,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
Validate.notNull(clazz, "Class cannot be null!");
|
||||
initialize();
|
||||
|
||||
Validate.notEmpty(REGISTRY, "Registry has no elements!");
|
||||
|
||||
for (Serializer serializer : REGISTRY) {
|
||||
if (serializer.getType().equals(clazz)
|
||||
&& serializer.isOptional() == optional) {
|
||||
@@ -955,7 +963,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
throw new IllegalArgumentException("No serializer found for " + (optional ? "Optional<" + clazz + ">" : clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -964,7 +972,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
* @return The serializer, or null if none exists
|
||||
*/
|
||||
public static Serializer fromHandle(Object handle) {
|
||||
Validate.notNull("handle cannot be null!");
|
||||
Validate.notNull(handle, "handle cannot be null!");
|
||||
initialize();
|
||||
|
||||
for (Serializer serializer : REGISTRY) {
|
||||
@@ -992,7 +1000,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
Type[] args = type.getActualTypeArguments();
|
||||
Type arg = args[0];
|
||||
|
||||
Class<?> innerClass = null;
|
||||
Class<?> innerClass;
|
||||
boolean optional = false;
|
||||
|
||||
if (arg instanceof Class<?>) {
|
||||
@@ -1012,6 +1020,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
throw new IllegalStateException("Failed to read field " + candidate);
|
||||
}
|
||||
|
||||
if (serializer == null) {
|
||||
throw new RuntimeException("Failed to read serializer: " + candidate.getName());
|
||||
}
|
||||
|
||||
REGISTRY.add(new Serializer(innerClass, serializer, optional));
|
||||
}
|
||||
}
|
||||
@@ -1024,7 +1036,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
* @return The serializer
|
||||
*/
|
||||
public static Serializer getChatComponentSerializer() {
|
||||
return get(MinecraftReflection.getIChatBaseComponentClass());
|
||||
return getChatComponentSerializer(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the serializer for IChatBaseComponents
|
||||
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
|
||||
* @return The serializer
|
||||
*/
|
||||
public static Serializer getChatComponentSerializer(boolean optional) {
|
||||
return get(MinecraftReflection.getIChatBaseComponentClass(), optional);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1078,5 +1099,13 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* Represents an immutable wrapped ParticleParam in 1.13
|
||||
*/
|
||||
public class WrappedParticle<T> {
|
||||
private static MethodAccessor toBukkit;
|
||||
private static MethodAccessor toNMS;
|
||||
private static MethodAccessor toCraftData;
|
||||
|
||||
private static void ensureMethods() {
|
||||
if (toBukkit != null && toNMS != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle"));
|
||||
FuzzyMethodContract contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.returnTypeExact(Particle.class)
|
||||
.parameterExactType(MinecraftReflection.getMinecraftClass("ParticleParam"))
|
||||
.build();
|
||||
toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
|
||||
contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.returnTypeExact(MinecraftReflection.getMinecraftClass("ParticleParam"))
|
||||
.parameterCount(2)
|
||||
.build();
|
||||
toNMS = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
|
||||
Class<?> cbData = MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData");
|
||||
fuzzy = FuzzyReflection.fromClass(cbData);
|
||||
contract = FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.returnTypeExact(cbData)
|
||||
.parameterExactArray(MinecraftReflection.getIBlockDataClass())
|
||||
.build();
|
||||
toCraftData = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
}
|
||||
|
||||
private final Particle particle;
|
||||
private final T data;
|
||||
private final Object handle;
|
||||
|
||||
private WrappedParticle(Object handle, Particle particle, T data) {
|
||||
this.handle = handle;
|
||||
this.particle = particle;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return This particle's Bukkit type
|
||||
*/
|
||||
public Particle getParticle() {
|
||||
return particle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this Particle's Bukkit/ProtocolLib data. The type of this data depends on the
|
||||
* {@link #getParticle() Particle type}. For Block particles it will be {@link WrappedBlockData},
|
||||
* for Item crack particles, it will be an {@link ItemStack}, and for redstone particles it will
|
||||
* be {@link Particle.DustOptions}
|
||||
*
|
||||
* @return The particle data
|
||||
*/
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NMS handle
|
||||
*/
|
||||
public Object getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public static WrappedParticle fromHandle(Object handle) {
|
||||
ensureMethods();
|
||||
|
||||
Particle bukkit = (Particle) toBukkit.invoke(null, handle);
|
||||
Object data = null;
|
||||
|
||||
switch (bukkit) {
|
||||
case BLOCK_CRACK:
|
||||
case BLOCK_DUST:
|
||||
case FALLING_DUST:
|
||||
data = getBlockData(handle);
|
||||
break;
|
||||
case ITEM_CRACK:
|
||||
data = getItem(handle);
|
||||
break;
|
||||
case REDSTONE:
|
||||
data = getRedstone(handle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return new WrappedParticle<>(handle, bukkit, data);
|
||||
}
|
||||
|
||||
private static WrappedBlockData getBlockData(Object handle) {
|
||||
return new StructureModifier<>(handle.getClass())
|
||||
.withTarget(handle)
|
||||
.withType(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter())
|
||||
.read(0);
|
||||
}
|
||||
|
||||
private static Object getItem(Object handle) {
|
||||
return new StructureModifier<>(handle.getClass())
|
||||
.withTarget(handle)
|
||||
.withType(MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter())
|
||||
.read(0);
|
||||
}
|
||||
|
||||
private static Object getRedstone(Object handle) {
|
||||
StructureModifier<Float> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class);
|
||||
return new Particle.DustOptions(Color.fromRGB(
|
||||
(int) (modifier.read(0) * 255),
|
||||
(int) (modifier.read(1) * 255),
|
||||
(int) (modifier.read(2) * 255)),
|
||||
modifier.read(3));
|
||||
}
|
||||
|
||||
public static <T> WrappedParticle<T> create(Particle particle, T data) {
|
||||
ensureMethods();
|
||||
|
||||
Object bukkitData = data;
|
||||
if (data instanceof WrappedBlockData) {
|
||||
WrappedBlockData blockData = (WrappedBlockData) data;
|
||||
bukkitData = toCraftData.invoke(null, blockData.getHandle());
|
||||
}
|
||||
|
||||
Object handle = toNMS.invoke(null, particle, bukkitData);
|
||||
return new WrappedParticle<>(handle, particle, data);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ import io.netty.handler.codec.base64.Base64;
|
||||
* Represents a server ping packet data.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class WrappedServerPing extends AbstractWrapper {
|
||||
public class WrappedServerPing extends AbstractWrapper implements ClonableWrapper {
|
||||
private static Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
|
||||
private static Class<?> GAME_PROFILE_ARRAY = MinecraftReflection.getArrayClass(GAME_PROFILE);
|
||||
|
||||
@@ -280,7 +280,7 @@ public class WrappedServerPing extends AbstractWrapper {
|
||||
public void setPlayers(Iterable<? extends WrappedGameProfile> profile) {
|
||||
if (players == null)
|
||||
resetPlayers();
|
||||
PLAYERS_PROFILES.set(players, (profile != null) ? PROFILE_CONVERT.getGeneric(GAME_PROFILE_ARRAY, profile) : null);
|
||||
PLAYERS_PROFILES.set(players, (profile != null) ? PROFILE_CONVERT.getGeneric(profile) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-5
@@ -147,10 +147,6 @@ public class WrappedSignedProperty extends AbstractWrapper {
|
||||
|
||||
@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() + "]";
|
||||
}
|
||||
}
|
||||
+13
-6
@@ -27,6 +27,9 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -235,6 +238,8 @@ public class WrappedWatchableObject extends AbstractWrapper {
|
||||
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
|
||||
@@ -268,20 +273,22 @@ public class WrappedWatchableObject extends AbstractWrapper {
|
||||
if (wrapped instanceof WrappedChatComponent) {
|
||||
return ((WrappedChatComponent) wrapped).getHandle();
|
||||
} else if (wrapped instanceof ItemStack) {
|
||||
return BukkitConverters.getItemStackConverter().getGeneric(MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
|
||||
return BukkitConverters.getItemStackConverter().getGeneric((ItemStack) wrapped);
|
||||
} else if (wrapped instanceof WrappedBlockData) {
|
||||
return BukkitConverters.getWrappedBlockDataConverter().getGeneric(MinecraftReflection.getIBlockDataClass(), (WrappedBlockData) wrapped);
|
||||
return BukkitConverters.getWrappedBlockDataConverter().getGeneric((WrappedBlockData) wrapped);
|
||||
} else if (wrapped instanceof Vector3F) {
|
||||
return Vector3F.getConverter().getGeneric(Vector3F.getMinecraftClass(), (Vector3F) wrapped);
|
||||
return Vector3F.getConverter().getGeneric((Vector3F) wrapped);
|
||||
} else if (wrapped instanceof BlockPosition) {
|
||||
return BlockPosition.getConverter().getGeneric(MinecraftReflection.getBlockPositionClass(), (BlockPosition) wrapped);
|
||||
return BlockPosition.getConverter().getGeneric((BlockPosition) wrapped);
|
||||
} else if (wrapped instanceof Direction) {
|
||||
return EnumWrappers.getDirectionConverter().getGeneric(EnumWrappers.getDirectionClass(), (Direction) wrapped);
|
||||
return EnumWrappers.getDirectionConverter().getGeneric((Direction) wrapped);
|
||||
} else if (wrapped instanceof NbtCompound) {
|
||||
return NbtFactory.fromBase((NbtCompound) wrapped).getHandle();
|
||||
}
|
||||
|
||||
// Legacy classes
|
||||
if (wrapped instanceof ChunkPosition) {
|
||||
return ChunkPosition.getConverter().getGeneric(MinecraftReflection.getChunkPositionClass(), (ChunkPosition) wrapped);
|
||||
return ChunkPosition.getConverter().getGeneric((ChunkPosition) wrapped);
|
||||
} else if (wrapped instanceof WrappedChunkCoordinate) {
|
||||
return ((WrappedChunkCoordinate) wrapped).getHandle();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
package com.comphenix.protocol.wrappers.nbt;
|
||||
|
||||
|
||||
import com.comphenix.protocol.wrappers.ClonableWrapper;
|
||||
|
||||
/**
|
||||
* Represents a generic container for an NBT element.
|
||||
* <p>
|
||||
@@ -26,7 +28,7 @@ package com.comphenix.protocol.wrappers.nbt;
|
||||
* @author Kristian
|
||||
* @param <TType> - type of the value that is stored.
|
||||
*/
|
||||
public interface NbtBase<TType> {
|
||||
public interface NbtBase<TType> extends ClonableWrapper {
|
||||
/**
|
||||
* Accepts a NBT visitor.
|
||||
* @param visitor - the hierarchical NBT visitor.
|
||||
@@ -78,10 +80,14 @@ public interface NbtBase<TType> {
|
||||
* @param newValue - the new value of this tag.
|
||||
*/
|
||||
public abstract void setValue(TType newValue);
|
||||
|
||||
|
||||
/**
|
||||
* Clone the current NBT tag.
|
||||
* @return The cloned tag.
|
||||
*/
|
||||
public abstract NbtBase<TType> deepClone();
|
||||
|
||||
default Object getHandle() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
@@ -133,7 +134,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 +152,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.
|
||||
*/
|
||||
@@ -168,6 +169,31 @@ public class NbtFactory {
|
||||
}
|
||||
return fromBase(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a wrapper for a NBT tag in an ItemStack. This is where auxillary
|
||||
* data such as enchantments, name, and lore is stored. It doesn't include the material,
|
||||
* damage value, or stack size.
|
||||
* <p>
|
||||
* This differs from {@link #fromItemTag(ItemStack)} in that the tag is not created if it
|
||||
* doesn't already exist.
|
||||
*
|
||||
* @param stack the ItemStack. Must be a CraftItemStack. Use {@link MinecraftReflection#getBukkitItemStack(Object)}
|
||||
* @return A wrapper for the NBT tag if it exists, an empty Optional if not
|
||||
*/
|
||||
public static Optional<NbtWrapper<?>> fromItemOptional(ItemStack stack) {
|
||||
checkItemStack(stack);
|
||||
|
||||
StructureModifier<NbtBase<?>> modifier = getStackModifier(stack);
|
||||
NbtBase<?> result = modifier.read(0);
|
||||
|
||||
// Create the tag if it doesn't exist
|
||||
if (result == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(fromBase(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a NBT compound from a GZIP compressed file.
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.comphenix.protocol.wrappers.nbt;
|
||||
|
||||
import java.io.DataOutput;
|
||||
|
||||
import com.comphenix.protocol.wrappers.ClonableWrapper;
|
||||
|
||||
/**
|
||||
* Indicates that this NBT wraps an underlying net.minecraft.server instance.
|
||||
* <p>
|
||||
@@ -28,7 +30,7 @@ import java.io.DataOutput;
|
||||
*
|
||||
* @param <TType> - type of the value that is stored.
|
||||
*/
|
||||
public interface NbtWrapper<TType> extends NbtBase<TType> {
|
||||
public interface NbtWrapper<TType> extends NbtBase<TType>, ClonableWrapper {
|
||||
/**
|
||||
* Retrieve the underlying net.minecraft.server instance.
|
||||
* @return The NMS instance.
|
||||
|
||||
+18
-19
@@ -4,25 +4,24 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.sf.cglib.asm.ClassReader;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
import net.sf.cglib.asm.Opcodes;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import net.sf.cglib.asm.$ClassReader;
|
||||
import net.sf.cglib.asm.$ClassVisitor;
|
||||
import net.sf.cglib.asm.$MethodVisitor;
|
||||
import net.sf.cglib.asm.$Opcodes;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import org.bukkit.block.BlockState;
|
||||
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Manipulate tile entities.
|
||||
* @author Kristian
|
||||
@@ -90,26 +89,26 @@ class TileEntityAccessor<T extends BlockState> {
|
||||
private void findMethodsUsingASM() throws IOException {
|
||||
final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass();
|
||||
final Class<?> tileEntityClass = MinecraftReflection.getTileEntityClass();
|
||||
final ClassReader reader = new ClassReader(tileEntityClass.getCanonicalName());
|
||||
final $ClassReader reader = new $ClassReader(tileEntityClass.getCanonicalName());
|
||||
|
||||
final String tagCompoundName = getJarName(MinecraftReflection.getNBTCompoundClass());
|
||||
final String expectedDesc = "(L" + tagCompoundName + ";)";
|
||||
|
||||
reader.accept(new EmptyClassVisitor() {
|
||||
reader.accept(new $ClassVisitor($Opcodes.ASM5) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
public $MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
final String methodName = name;
|
||||
|
||||
// Detect read/write calls to NBTTagCompound
|
||||
if (desc.startsWith(expectedDesc)) {
|
||||
return new EmptyMethodVisitor() {
|
||||
return new $MethodVisitor($Opcodes.ASM5) {
|
||||
private int readMethods;
|
||||
private int writeMethods;
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean intf) {
|
||||
// This must be a virtual call on NBTTagCompound that accepts a String
|
||||
if (opcode == Opcodes.INVOKEVIRTUAL
|
||||
if (opcode == $Opcodes.INVOKEVIRTUAL
|
||||
&& tagCompoundName.equals(owner)
|
||||
&& desc.startsWith("(Ljava/lang/String")) {
|
||||
|
||||
|
||||
+19
-44
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<name>ProtocolLib</name>
|
||||
<version>4.2.1</version>
|
||||
<version>4.4.0</version>
|
||||
|
||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
|
||||
@@ -12,7 +12,7 @@
|
||||
<parent>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib-Parent</artifactId>
|
||||
<version>v4-SNAPSHOT</version>
|
||||
<version>4.4.0-SNAPSHOT</version>
|
||||
<relativePath>../../</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.build.number></project.build.number>
|
||||
<project.fullVersion>${project.version}</project.fullVersion>
|
||||
|
||||
<powermock.version>1.7.4</powermock.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@@ -40,7 +42,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<version>3.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@@ -63,6 +65,7 @@
|
||||
<exclude>org.spigotmc:spigot</exclude>
|
||||
<exclude>org.spigotmc:spigot-api</exclude>
|
||||
<exclude>junit:junit</exclude>
|
||||
<exclude>io.netty:*</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
@@ -72,17 +75,17 @@
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
@@ -99,7 +102,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.10</version>
|
||||
<version>2.22.0</version>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<property>
|
||||
@@ -230,60 +233,32 @@
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib-API</artifactId>
|
||||
<version>${project.version}</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.11.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.11.2-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>
|
||||
|
||||
<!-- Testing dependencies -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.10</version>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.8.4</version>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>2.21.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>1.5</version>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito</artifactId>
|
||||
<version>1.5</version>
|
||||
<artifactId>powermock-api-mockito2</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -23,19 +23,7 @@ import java.text.MessageFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
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;
|
||||
import java.util.logging.*;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
@@ -45,6 +33,13 @@ 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
|
||||
@@ -99,16 +94,16 @@ public class PacketLogging implements CommandExecutor, PacketListener {
|
||||
type = PacketType.findCurrent(protocol, pSender, id);
|
||||
} catch (NumberFormatException ex) { // Check packet names
|
||||
String name = args[2];
|
||||
outer: for (PacketType packet : PacketType.values()) {
|
||||
for (PacketType packet : PacketType.values()) {
|
||||
if (packet.getProtocol() == protocol && packet.getSender() == pSender) {
|
||||
if (packet.name().equalsIgnoreCase(name)) {
|
||||
type = packet;
|
||||
break outer;
|
||||
break;
|
||||
}
|
||||
for (String className : packet.getClassNames()) {
|
||||
if (className.equalsIgnoreCase(name)) {
|
||||
type = packet;
|
||||
break outer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,9 +243,8 @@ public class PacketLogging implements CommandExecutor, PacketListener {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
private static enum LogLocation {
|
||||
CONSOLE,
|
||||
FILE;
|
||||
private enum LogLocation {
|
||||
CONSOLE, FILE
|
||||
}
|
||||
|
||||
private static class LogFormatter extends Formatter {
|
||||
|
||||
@@ -52,7 +52,6 @@ import com.comphenix.protocol.updater.Updater.UpdateType;
|
||||
import com.comphenix.protocol.utility.ChatExtensions;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.Util;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -155,14 +154,6 @@ public class ProtocolLib extends JavaPlugin {
|
||||
logger = getLogger();
|
||||
ProtocolLogger.init(this);
|
||||
|
||||
int java = Util.getJavaVersion();
|
||||
if (java != -1 && java < 8 && !getConfig().getBoolean("ignoreJava", false)) {
|
||||
logger.warning("Detected outdated Java version: Java " + java);
|
||||
logger.warning("It is recommended that you update to Java 8 as soon as possible.");
|
||||
logger.warning("Future versions of ProtocolLib many not support Java " + java + ".");
|
||||
logger.warning("Java 8 will allow for much faster reflection performance.");
|
||||
}
|
||||
|
||||
// Initialize enhancer factory
|
||||
EnhancerFactory.getInstance().setClassLoader(getClassLoader());
|
||||
|
||||
@@ -246,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();
|
||||
@@ -277,8 +266,6 @@ public class ProtocolLib extends JavaPlugin {
|
||||
}
|
||||
} 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) {
|
||||
@@ -419,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();
|
||||
@@ -434,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) {
|
||||
@@ -502,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()) {
|
||||
@@ -585,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));
|
||||
@@ -673,4 +654,8 @@ public class ProtocolLib extends JavaPlugin {
|
||||
public Statistics getStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
public ProtocolConfig getProtocolConfig() {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
+49
-183
@@ -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.
|
||||
|
||||
+141
-157
@@ -16,35 +16,12 @@
|
||||
*/
|
||||
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;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
@@ -55,7 +32,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;
|
||||
@@ -68,18 +44,30 @@ import com.comphenix.protocol.utility.MinecraftProtocolVersion;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.*;
|
||||
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.internal.TypeParameterMatcher;
|
||||
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents a channel injector.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
public static final ReportType REPORT_CANNOT_INTERCEPT_SERVER_PACKET = new ReportType("Unable to intercept a written server packet.");
|
||||
public static final ReportType REPORT_CANNOT_INTERCEPT_CLIENT_PACKET = new ReportType("Unable to intercept a read client packet.");
|
||||
public static final ReportType REPORT_CANNOT_EXECUTE_IN_CHANNEL_THREAD = new ReportType("Cannot execute code in channel thread.");
|
||||
public static final ReportType REPORT_CANNOT_FIND_GET_VERSION = new ReportType("Cannot find getVersion() in NetworkMananger");
|
||||
public static final ReportType REPORT_CANNOT_SEND_PACKET = new ReportType("Unable to send packet %s to %s");
|
||||
private static final ReportType REPORT_CANNOT_INTERCEPT_SERVER_PACKET = new ReportType("Unable to intercept a written server packet.");
|
||||
private static final ReportType REPORT_CANNOT_INTERCEPT_CLIENT_PACKET = new ReportType("Unable to intercept a read client packet.");
|
||||
private static final ReportType REPORT_CANNOT_EXECUTE_IN_CHANNEL_THREAD = new ReportType("Cannot execute code in channel thread.");
|
||||
private static final ReportType REPORT_CANNOT_SEND_PACKET = new ReportType("Unable to send packet %s to %s");
|
||||
|
||||
/**
|
||||
* Indicates that a packet has bypassed packet listeners.
|
||||
@@ -90,6 +78,16 @@ 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 AtomicInteger keyId = new AtomicInteger();
|
||||
private static AttributeKey<Integer> PROTOCOL_KEY;
|
||||
|
||||
static {
|
||||
PROTOCOL_KEY = AttributeKey.valueOf("PROTOCOL-" + keyId.getAndIncrement());
|
||||
}
|
||||
|
||||
// Saved accessors
|
||||
private static MethodAccessor DECODE_BUFFER;
|
||||
private static MethodAccessor ENCODE_BUFFER;
|
||||
@@ -98,9 +96,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;
|
||||
|
||||
@@ -118,7 +113,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
private VolatileField channelField;
|
||||
|
||||
// Known network markers
|
||||
private ConcurrentMap<Object, NetworkMarker> packetMarker = new MapMaker().weakKeys().makeMap();
|
||||
private Map<Object, NetworkMarker> packetMarker = new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
* Indicate that this packet has been processed by event listeners.
|
||||
@@ -135,18 +130,13 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
/**
|
||||
* A flag set by the main thread to indiciate that a packet should not be processed.
|
||||
*/
|
||||
private final ThreadLocal<Boolean> scheduleProcessPackets = new ThreadLocal<Boolean>() {
|
||||
@Override
|
||||
protected Boolean initialValue() {
|
||||
return true;
|
||||
};
|
||||
};
|
||||
private final ThreadLocal<Boolean> scheduleProcessPackets = ThreadLocal.withInitial(() -> true);
|
||||
|
||||
// Other handlers
|
||||
private ByteToMessageDecoder vanillaDecoder;
|
||||
private MessageToByteEncoder<Object> vanillaEncoder;
|
||||
|
||||
private Deque<PacketEvent> finishQueue = new ArrayDeque<PacketEvent>();
|
||||
private Deque<PacketEvent> finishQueue = new ArrayDeque<>();
|
||||
|
||||
// The channel listener
|
||||
private ChannelListener channelListener;
|
||||
@@ -166,7 +156,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
* @param channelListener - a listener.
|
||||
* @param factory - the factory that created this injector
|
||||
*/
|
||||
public ChannelInjector(Player player, Object networkManager, Channel channel, ChannelListener channelListener, InjectionFactory factory) {
|
||||
ChannelInjector(Player player, Object networkManager, Channel channel, ChannelListener channelListener, InjectionFactory factory) {
|
||||
this.player = Preconditions.checkNotNull(player, "player cannot be NULL");
|
||||
this.networkManager = Preconditions.checkNotNull(networkManager, "networkMananger cannot be NULL");
|
||||
this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL");
|
||||
@@ -175,8 +165,9 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
this.processor = new NetworkProcessor(ProtocolLibrary.getErrorReporter());
|
||||
|
||||
// Get the channel field
|
||||
this.channelField = new VolatileField(FuzzyReflection.fromObject(networkManager, true).getFieldByType("channel", Channel.class),
|
||||
networkManager, true);
|
||||
this.channelField = new VolatileField(FuzzyReflection
|
||||
.fromObject(networkManager, true)
|
||||
.getFieldByType("channel", Channel.class), networkManager, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,19 +176,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
|
||||
@@ -215,12 +195,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
// pipeline with only some of the handlers removed
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
// Just like in the close() method, we'll avoid blocking the main thread
|
||||
executeInChannelThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
inject();
|
||||
}
|
||||
});
|
||||
executeInChannelThread(this::inject);
|
||||
return false; // We don't know
|
||||
}
|
||||
|
||||
@@ -234,9 +209,9 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
vanillaEncoder = (MessageToByteEncoder<Object>) originalChannel.pipeline().get("encoder");
|
||||
|
||||
if (vanillaDecoder == null)
|
||||
throw new IllegalArgumentException("Unable to find vanilla decoder in " + originalChannel.pipeline() );
|
||||
throw new IllegalArgumentException("Unable to find vanilla decoder in " + originalChannel.pipeline());
|
||||
if (vanillaEncoder == null)
|
||||
throw new IllegalArgumentException("Unable to find vanilla encoder in " + originalChannel.pipeline() );
|
||||
throw new IllegalArgumentException("Unable to find vanilla encoder in " + originalChannel.pipeline());
|
||||
patchEncoder(vanillaEncoder);
|
||||
|
||||
if (DECODE_BUFFER == null)
|
||||
@@ -249,7 +224,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
// Intercept sent packets
|
||||
MessageToByteEncoder<Object> protocolEncoder = new MessageToByteEncoder<Object>() {
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
|
||||
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) {
|
||||
if (packet instanceof WirePacket) {
|
||||
// Special case for wire format
|
||||
ChannelInjector.this.encodeWirePacket((WirePacket) packet, output);
|
||||
@@ -261,17 +236,17 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
|
||||
super.write(ctx, packet, promise);
|
||||
ChannelInjector.this.finalWrite(ctx, packet, promise);
|
||||
ChannelInjector.this.finalWrite();
|
||||
}
|
||||
};
|
||||
|
||||
// Intercept recieved packets
|
||||
ChannelInboundHandlerAdapter finishHandler = new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
// Execute context first
|
||||
ctx.fireChannelRead(msg);
|
||||
ChannelInjector.this.finishRead(ctx, msg);
|
||||
ChannelInjector.this.finishRead();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -311,17 +286,14 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
if (event != null && event.isCancelled())
|
||||
return null;
|
||||
|
||||
return new Callable<T>() {
|
||||
@Override
|
||||
public T call() throws Exception {
|
||||
T result = null;
|
||||
return () -> {
|
||||
T result;
|
||||
|
||||
// This field must only be updated in the pipeline thread
|
||||
currentEvent = event;
|
||||
result = callable.call();
|
||||
currentEvent = null;
|
||||
return result;
|
||||
}
|
||||
// This field must only be updated in the pipeline thread
|
||||
currentEvent = event;
|
||||
result = callable.call();
|
||||
currentEvent = null;
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -333,17 +305,14 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
if (event != null && event.isCancelled())
|
||||
return null;
|
||||
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
currentEvent = event;
|
||||
runnable.run();
|
||||
currentEvent = null;
|
||||
}
|
||||
return () -> {
|
||||
currentEvent = event;
|
||||
runnable.run();
|
||||
currentEvent = null;
|
||||
};
|
||||
}
|
||||
|
||||
protected PacketEvent handleScheduled(Object instance, FieldAccessor accessor) {
|
||||
PacketEvent handleScheduled(Object instance, FieldAccessor accessor) {
|
||||
// Let the filters handle this packet
|
||||
Object original = accessor.get(instance);
|
||||
|
||||
@@ -359,15 +328,17 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
return BYPASSED_PACKET;
|
||||
}
|
||||
}
|
||||
PacketEvent event = processSending(original);
|
||||
|
||||
PacketEvent event = processSending(original);
|
||||
if (event != null && !event.isCancelled()) {
|
||||
Object changed = event.getPacket().getHandle();
|
||||
|
||||
// Change packet to be scheduled
|
||||
if (original != changed)
|
||||
if (original != changed) {
|
||||
accessor.set(instance, changed);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return event != null ? event : BYPASSED_PACKET;
|
||||
}
|
||||
});
|
||||
@@ -404,17 +375,20 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
if (ENCODER_TYPE_MATCHER == null) {
|
||||
ENCODER_TYPE_MATCHER = Accessors.getFieldAccessor(encoder.getClass(), "matcher", true);
|
||||
}
|
||||
|
||||
ENCODER_TYPE_MATCHER.set(encoder, TypeParameterMatcher.get(MinecraftReflection.getPacketClass()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
if (channelListener.isDebug())
|
||||
if (channelListener.isDebug()) {
|
||||
cause.printStackTrace();
|
||||
}
|
||||
|
||||
super.exceptionCaught(ctx, cause);
|
||||
}
|
||||
|
||||
protected void encodeWirePacket(WirePacket packet, ByteBuf output) throws Exception {
|
||||
private void encodeWirePacket(WirePacket packet, ByteBuf output) {
|
||||
packet.writeId(output);
|
||||
packet.writeBytes(output);
|
||||
}
|
||||
@@ -424,9 +398,8 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
* @param ctx - the current context.
|
||||
* @param packet - the packet to encode to a byte array.
|
||||
* @param output - the output byte array.
|
||||
* @throws Exception If anything went wrong.
|
||||
*/
|
||||
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
|
||||
private void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) {
|
||||
NetworkMarker marker = null;
|
||||
PacketEvent event = currentEvent;
|
||||
|
||||
@@ -455,6 +428,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event != null) {
|
||||
// Retrieve marker without accidentally constructing it
|
||||
marker = NetworkMarker.getNetworkMarker(event);
|
||||
@@ -474,7 +448,6 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
|
||||
// Sent listeners?
|
||||
finalEvent = event;
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
channelListener.getReporter().reportDetailed(this,
|
||||
@@ -489,12 +462,9 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a packet has been written to the channel.
|
||||
* @param ctx - current context.
|
||||
* @param packet - the packet that has been written.
|
||||
* @param promise - a promise.
|
||||
* Invoked when a packet has been written to the channel
|
||||
*/
|
||||
protected void finalWrite(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) {
|
||||
private void finalWrite() {
|
||||
PacketEvent event = finalEvent;
|
||||
|
||||
if (event != null) {
|
||||
@@ -508,17 +478,15 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
|
||||
private void scheduleMainThread(final Object packetCopy) {
|
||||
// Don't use BukkitExecutors for this - it has a bit of overhead
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(factory.getPlugin(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(factory.getPlugin(), () -> {
|
||||
if (!closed) {
|
||||
invokeSendPacket(packetCopy);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List<Object> packets) throws Exception {
|
||||
byteBuffer.markReaderIndex();
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List<Object> packets) {
|
||||
DECODE_BUFFER.invoke(vanillaDecoder, ctx, byteBuffer, packets);
|
||||
|
||||
try {
|
||||
@@ -544,12 +512,13 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
if (output != null) {
|
||||
if (output.isCancelled()) {
|
||||
it.remove();
|
||||
continue;
|
||||
} else if (output.getPacket().getHandle() != input) {
|
||||
it.set(output.getPacket().getHandle());
|
||||
}
|
||||
} else {
|
||||
if (output.getPacket().getHandle() != input) {
|
||||
it.set(output.getPacket().getHandle());
|
||||
}
|
||||
|
||||
finishQueue.addLast(output);
|
||||
finishQueue.addLast(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -559,11 +528,9 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked after our decoder.
|
||||
* @param ctx - current context.
|
||||
* @param msg - the current packet.
|
||||
* Invoked after our decoder
|
||||
*/
|
||||
protected void finishRead(ChannelHandlerContext ctx, Object msg) {
|
||||
private void finishRead() {
|
||||
// Assume same order
|
||||
PacketEvent event = finishQueue.pollFirst();
|
||||
|
||||
@@ -581,7 +548,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
* @param packetClass - the packet class.
|
||||
* @param packet - the packet.
|
||||
*/
|
||||
protected void handleLogin(Class<?> packetClass, Object packet) {
|
||||
private void handleLogin(Class<?> packetClass, Object packet) {
|
||||
// Try to find the login packet class
|
||||
if (PACKET_LOGIN_CLIENT == null) {
|
||||
PACKET_LOGIN_CLIENT = PacketType.Login.Client.START.getPacketClass();
|
||||
@@ -603,6 +570,24 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
// Save the channel injector
|
||||
factory.cacheInjector(profile.getName(), this);
|
||||
}
|
||||
|
||||
if (PACKET_SET_PROTOCOL == null) {
|
||||
try {
|
||||
PACKET_SET_PROTOCOL = PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass();
|
||||
} 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -663,10 +648,12 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
* @param packet - the packet to send.
|
||||
*/
|
||||
private void invokeSendPacket(Object packet) {
|
||||
Validate.isTrue(!closed, "cannot send packets to a closed channel");
|
||||
|
||||
// Attempt to send the packet with NetworkMarker.handle(), or the PlayerConnection if its active
|
||||
try {
|
||||
if (player instanceof Factory) {
|
||||
MinecraftMethods.getNetworkManagerHandleMethod().invoke(networkManager, packet, new GenericFutureListener[0]);
|
||||
MinecraftMethods.getNetworkManagerHandleMethod().invoke(networkManager, packet);
|
||||
} else {
|
||||
MinecraftMethods.getSendPacketMethod().invoke(getPlayerConnection(), packet);
|
||||
}
|
||||
@@ -681,15 +668,12 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
// TODO: Ensure the packet listeners are executed in the channel thread.
|
||||
|
||||
// Execute this in the channel thread
|
||||
Runnable action = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
MinecraftMethods.getNetworkManagerReadPacketMethod().invoke(networkManager, null, packet);
|
||||
} catch (Exception e) {
|
||||
// Inform the user
|
||||
ProtocolLibrary.getErrorReporter().reportMinimal(factory.getPlugin(), "recieveClientPacket", e);
|
||||
}
|
||||
Runnable action = () -> {
|
||||
try {
|
||||
MinecraftMethods.getNetworkManagerReadPacketMethod().invoke(networkManager, null, packet);
|
||||
} catch (Exception e) {
|
||||
// Inform the user
|
||||
ProtocolLibrary.getErrorReporter().reportMinimal(factory.getPlugin(), "recieveClientPacket", e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -716,8 +700,14 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
*/
|
||||
private Object getPlayerConnection() {
|
||||
if (playerConnection == null) {
|
||||
playerConnection = MinecraftFields.getPlayerConnection(getPlayer());
|
||||
Player player = getPlayer();
|
||||
if (player == null) {
|
||||
throw new IllegalStateException("cannot send packet to offline player" + (playerName != null ? " " + playerName : ""));
|
||||
}
|
||||
|
||||
playerConnection = MinecraftFields.getPlayerConnection(player);
|
||||
}
|
||||
|
||||
return playerConnection;
|
||||
}
|
||||
|
||||
@@ -736,7 +726,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
if (player == null && playerName != null) {
|
||||
return Bukkit.getPlayer(playerName);
|
||||
return Bukkit.getPlayerExact(playerName);
|
||||
}
|
||||
|
||||
return player;
|
||||
@@ -801,19 +791,16 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
// the worker thread is waiting for the main thread to finish executing PlayerQuitEvent.
|
||||
//
|
||||
// TL;DR: Concurrency is hard.
|
||||
executeInChannelThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String[] handlers = new String[] {
|
||||
"protocol_lib_decoder", "protocol_lib_finish", "protocol_lib_encoder"
|
||||
};
|
||||
executeInChannelThread(() -> {
|
||||
String[] handlers = new String[] {
|
||||
"protocol_lib_decoder", "protocol_lib_finish", "protocol_lib_encoder"
|
||||
};
|
||||
|
||||
for (String handler : handlers) {
|
||||
try {
|
||||
originalChannel.pipeline().remove(handler);
|
||||
} catch (NoSuchElementException e) {
|
||||
// Ignore
|
||||
}
|
||||
for (String handler : handlers) {
|
||||
try {
|
||||
originalChannel.pipeline().remove(handler);
|
||||
} catch (NoSuchElementException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -836,15 +823,12 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
* @param command - the command to execute.
|
||||
*/
|
||||
private void executeInChannelThread(final Runnable command) {
|
||||
originalChannel.eventLoop().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
command.run();
|
||||
} catch (Exception e) {
|
||||
ProtocolLibrary.getErrorReporter().reportDetailed(ChannelInjector.this,
|
||||
originalChannel.eventLoop().execute(() -> {
|
||||
try {
|
||||
command.run();
|
||||
} catch (Exception e) {
|
||||
ProtocolLibrary.getErrorReporter().reportDetailed(ChannelInjector.this,
|
||||
Report.newBuilder(REPORT_CANNOT_EXECUTE_IN_CHANNEL_THREAD).error(e).build());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -855,7 +839,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
* @param clazz - the type.
|
||||
* @return The first handler, or NULL.
|
||||
*/
|
||||
public static ChannelHandler findChannelHandler(Channel channel, Class<?> clazz) {
|
||||
static ChannelHandler findChannelHandler(Channel channel, Class<?> clazz) {
|
||||
for (Entry<String, ChannelHandler> entry : channel.pipeline()) {
|
||||
if (clazz.isAssignableFrom(entry.getValue().getClass())) {
|
||||
return entry.getValue();
|
||||
@@ -871,27 +855,27 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
public static class ChannelSocketInjector implements SocketInjector {
|
||||
private final ChannelInjector injector;
|
||||
|
||||
public ChannelSocketInjector(ChannelInjector injector) {
|
||||
ChannelSocketInjector(ChannelInjector injector) {
|
||||
this.injector = Preconditions.checkNotNull(injector, "injector cannot be NULL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket getSocket() throws IllegalAccessException {
|
||||
public Socket getSocket() {
|
||||
return SocketAdapter.adapt((SocketChannel) injector.originalChannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getAddress() throws IllegalAccessException {
|
||||
public SocketAddress getAddress() {
|
||||
return injector.originalChannel.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(String message) throws InvocationTargetException {
|
||||
public void disconnect(String message) {
|
||||
injector.disconnect(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException {
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
injector.sendServerPacket(packet, marker, filtered);
|
||||
}
|
||||
|
||||
@@ -902,7 +886,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
|
||||
@Override
|
||||
public Player getUpdatedPlayer() {
|
||||
return injector.updated;
|
||||
return injector.updated != null ? injector.updated : getPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -915,7 +899,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
injector.setPlayer(updatedPlayer);
|
||||
}
|
||||
|
||||
public ChannelInjector getChannelInjector() {
|
||||
ChannelInjector getChannelInjector() {
|
||||
return injector;
|
||||
}
|
||||
}
|
||||
|
||||
+27
-9
@@ -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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
+6
@@ -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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
+28
-6
@@ -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();
|
||||
}
|
||||
*/
|
||||
}
|
||||
+49
-20
@@ -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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ public abstract class AbstractInputStreamLookup {
|
||||
Player player = previous.getPlayer();
|
||||
|
||||
// Default implementation
|
||||
if (player instanceof InjectorContainer) {
|
||||
if (player instanceof TemporaryPlayer) {
|
||||
TemporaryPlayerFactory.setInjectorInPlayer(player, current);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -47,8 +47,8 @@ public class TemporaryPlayerFactory {
|
||||
* @return The referenced player injector, or NULL if none can be found.
|
||||
*/
|
||||
public static SocketInjector getInjectorFromPlayer(Player player) {
|
||||
if (player instanceof InjectorContainer) {
|
||||
return ((InjectorContainer) player).getInjector();
|
||||
if (player instanceof TemporaryPlayer) {
|
||||
return ((TemporaryPlayer) player).getInjector();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public class TemporaryPlayerFactory {
|
||||
* @param injector - the injector to store.
|
||||
*/
|
||||
public static void setInjectorInPlayer(Player player, SocketInjector injector) {
|
||||
((InjectorContainer) player).setInjector(injector);
|
||||
((TemporaryPlayer) player).setInjector(injector);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,7 +88,7 @@ public class TemporaryPlayerFactory {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
String methodName = method.getName();
|
||||
SocketInjector injector = ((InjectorContainer) obj).getInjector();
|
||||
SocketInjector injector = ((TemporaryPlayer) obj).getInjector();
|
||||
|
||||
if (injector == null)
|
||||
throw new IllegalStateException("Unable to find injector.");
|
||||
@@ -152,7 +152,7 @@ public class TemporaryPlayerFactory {
|
||||
public int accept(Method method) {
|
||||
// Do not override the object method or the superclass methods
|
||||
if (method.getDeclaringClass().equals(Object.class) ||
|
||||
method.getDeclaringClass().equals(InjectorContainer.class))
|
||||
method.getDeclaringClass().equals(TemporaryPlayer.class))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
@@ -162,7 +162,7 @@ public class TemporaryPlayerFactory {
|
||||
|
||||
// CGLib is amazing
|
||||
Enhancer ex = new Enhancer();
|
||||
ex.setSuperclass(InjectorContainer.class);
|
||||
ex.setSuperclass(TemporaryPlayer.class);
|
||||
ex.setInterfaces(new Class[] { Player.class });
|
||||
ex.setCallbacks(new Callback[] { NoOp.INSTANCE, implementation });
|
||||
ex.setCallbackFilter(callbackFilter);
|
||||
@@ -179,7 +179,7 @@ public class TemporaryPlayerFactory {
|
||||
public Player createTemporaryPlayer(Server server, SocketInjector injector) {
|
||||
Player temporary = createTemporaryPlayer(server);
|
||||
|
||||
((InjectorContainer) temporary).setInjector(injector);
|
||||
((TemporaryPlayer) temporary).setInjector(injector);
|
||||
return temporary;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -21,54 +21,49 @@ import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLib;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.metrics.Metrics.Graph;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class Statistics {
|
||||
|
||||
// Metrics
|
||||
private Metrics metrics;
|
||||
|
||||
public Statistics(Plugin plugin) throws IOException {
|
||||
|
||||
public Statistics(ProtocolLib plugin) throws IOException {
|
||||
metrics = new Metrics(plugin);
|
||||
|
||||
metrics.logFailedRequests(plugin.getProtocolConfig().isDebug());
|
||||
|
||||
// Determine who is using this library
|
||||
addPluginUserGraph(metrics);
|
||||
|
||||
metrics.start();
|
||||
}
|
||||
|
||||
|
||||
private void addPluginUserGraph(Metrics metrics) {
|
||||
|
||||
Graph pluginUsers = metrics.createGraph("Plugin Users");
|
||||
|
||||
for (Map.Entry<String, Integer> entry : getPluginUsers(ProtocolLibrary.getProtocolManager()).entrySet()) {
|
||||
final int count = entry.getValue();
|
||||
|
||||
// Plot plugins of this type
|
||||
pluginUsers.addPlotter(new Metrics.Plotter(entry.getKey()) {
|
||||
@Override
|
||||
public int getValue() {
|
||||
return count;
|
||||
}
|
||||
});
|
||||
metrics.addCustomChart(new Metrics.AdvancedPie("Plugin Users", this::getPluginUsers));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("buildVersion", () -> splitVersion().getRight()));
|
||||
}
|
||||
|
||||
public static Pair<String, String> splitVersion() {
|
||||
String version = ProtocolLibrary.getPlugin().getDescription().getVersion();
|
||||
if (version.contains("-b")) {
|
||||
String[] split = version.split("-b");
|
||||
return Pair.of(split[0], split[1]);
|
||||
} else {
|
||||
return Pair.of(version, "Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Retrieve loaded plugins
|
||||
private Map<String, Integer> getPluginUsers(ProtocolManager manager) {
|
||||
|
||||
Map<String, Integer> users = new HashMap<String, Integer>();
|
||||
|
||||
for (PacketListener listener : manager.getPacketListeners()) {
|
||||
|
||||
private Map<String, Integer> getPluginUsers() {
|
||||
Map<String, Integer> users = new HashMap<>();
|
||||
|
||||
for (PacketListener listener : ProtocolLibrary.getProtocolManager().getPacketListeners()) {
|
||||
String name = PacketAdapter.getPluginName(listener);
|
||||
|
||||
|
||||
// Increment occurence
|
||||
if (!users.containsKey(name)) {
|
||||
users.put(name, 1);
|
||||
@@ -76,7 +71,7 @@ public class Statistics {
|
||||
users.put(name, users.get(name) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return users;
|
||||
}
|
||||
}
|
||||
+8
-131
@@ -7,19 +7,13 @@
|
||||
// Somewhat modified by aadnk.
|
||||
package com.comphenix.protocol.updater;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
@@ -27,9 +21,6 @@ import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.JSONValue;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
|
||||
/**
|
||||
* Check dev.bukkit.org to find updates for a given plugin, and download the updates if needed.
|
||||
* <p/>
|
||||
@@ -60,7 +51,7 @@ public class BukkitUpdater extends Updater {
|
||||
private static final String VERSION_VALUE = "gameVersion"; // Gets remote file's build version
|
||||
private static final Object FILE_NAME = "fileName"; // Gets remote file's name
|
||||
private static final String QUERY = "/servermods/files?projectIds="; // Path to GET
|
||||
private static final String HOST = "https://api.curseforge.com"; // Slugs will be appended to this to get to the project's RSS feed
|
||||
private static final String HOST = "https://servermods.forgesvc.net"; // Formerly api.curseforge.net
|
||||
|
||||
// private static final String[] NO_UPDATE_TAG = { "-DEV", "-PRE", "-SNAPSHOT" }; // If the version number contains one of these, don't update.
|
||||
private static final int BYTE_SIZE = 1024; // Used for downloading files
|
||||
@@ -71,7 +62,7 @@ public class BukkitUpdater extends Updater {
|
||||
/**
|
||||
* Initialize the updater.
|
||||
* <p>
|
||||
* Call {@link #start()} to actually start looking (and downloading) updates.
|
||||
* Call {@link #start(UpdateType)} to actually start looking (and downloading) updates.
|
||||
*
|
||||
* @param plugin The plugin that is checking for an update.
|
||||
* @param id The dev.bukkit.org id of the project
|
||||
@@ -186,18 +177,6 @@ public class BukkitUpdater extends Updater {
|
||||
this.plugin.getLogger().info("Downloading update: " + percent + "% of " + fileLength + " bytes.");
|
||||
}
|
||||
}
|
||||
//Just a quick check to make sure we didn't leave any files from last time...
|
||||
for (final File xFile : new File(this.plugin.getDataFolder().getParent(), this.updateFolder).listFiles()) {
|
||||
if (xFile.getName().endsWith(".zip")) {
|
||||
xFile.delete();
|
||||
}
|
||||
}
|
||||
// Check to see if it's a zip file, if it is, unzip it.
|
||||
final File dFile = new File(folder.getAbsolutePath() + "/" + file);
|
||||
if (dFile.getName().endsWith(".zip")) {
|
||||
// Unzip
|
||||
this.unzip(dFile.getCanonicalPath());
|
||||
}
|
||||
if (this.announce) {
|
||||
this.plugin.getLogger().info("Finished updating.");
|
||||
}
|
||||
@@ -217,107 +196,6 @@ public class BukkitUpdater extends Updater {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of Zip-File-Extractor, modified by Gravity for use with Bukkit
|
||||
*/
|
||||
private void unzip(String file) {
|
||||
try {
|
||||
final File fSourceZip = new File(file);
|
||||
final String zipPath = file.substring(0, file.length() - 4);
|
||||
ZipFile zipFile = new ZipFile(fSourceZip);
|
||||
Enumeration<? extends ZipEntry> e = zipFile.entries();
|
||||
while (e.hasMoreElements()) {
|
||||
ZipEntry entry = e.nextElement();
|
||||
File destinationFilePath = new File(zipPath, entry.getName());
|
||||
destinationFilePath.getParentFile().mkdirs();
|
||||
if (entry.isDirectory()) {
|
||||
continue;
|
||||
} else {
|
||||
final BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
|
||||
int b;
|
||||
final byte buffer[] = new byte[BukkitUpdater.BYTE_SIZE];
|
||||
final FileOutputStream fos = new FileOutputStream(destinationFilePath);
|
||||
final BufferedOutputStream bos = new BufferedOutputStream(fos, BukkitUpdater.BYTE_SIZE);
|
||||
while ((b = bis.read(buffer, 0, BukkitUpdater.BYTE_SIZE)) != -1) {
|
||||
bos.write(buffer, 0, b);
|
||||
}
|
||||
bos.flush();
|
||||
bos.close();
|
||||
bis.close();
|
||||
final String name = destinationFilePath.getName();
|
||||
if (name.endsWith(".jar") && this.pluginFile(name)) {
|
||||
destinationFilePath.renameTo(new File(this.plugin.getDataFolder().getParent(), this.updateFolder + "/" + name));
|
||||
}
|
||||
}
|
||||
entry = null;
|
||||
destinationFilePath = null;
|
||||
}
|
||||
e = null;
|
||||
zipFile.close();
|
||||
zipFile = null;
|
||||
|
||||
// Move any plugin data folders that were included to the right place, Bukkit won't do this for us.
|
||||
for (final File dFile : new File(zipPath).listFiles()) {
|
||||
if (dFile.isDirectory()) {
|
||||
if (this.pluginFile(dFile.getName())) {
|
||||
final File oFile = new File(this.plugin.getDataFolder().getParent(), dFile.getName()); // Get current dir
|
||||
final File[] contents = oFile.listFiles(); // List of existing files in the current dir
|
||||
for (final File cFile : dFile.listFiles()) // Loop through all the files in the new dir
|
||||
{
|
||||
boolean found = false;
|
||||
for (final File xFile : contents) // Loop through contents to see if it exists
|
||||
{
|
||||
if (xFile.getName().equals(cFile.getName())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// Move the new file into the current dir
|
||||
cFile.renameTo(new File(oFile.getCanonicalFile() + "/" + cFile.getName()));
|
||||
} else {
|
||||
// This file already exists, so we don't need it anymore.
|
||||
cFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dFile.delete();
|
||||
}
|
||||
new File(zipPath).delete();
|
||||
fSourceZip.delete();
|
||||
} catch (final IOException ex) {
|
||||
this.plugin.getLogger().warning("The auto-updater tried to unzip a new update file, but was unsuccessful.");
|
||||
this.result = BukkitUpdater.UpdateResult.FAIL_DOWNLOAD;
|
||||
ex.printStackTrace();
|
||||
}
|
||||
new File(file).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the name of a jar is one of the plugins currently installed, used for extracting the correct files out of a zip.
|
||||
*/
|
||||
private boolean pluginFile(String name) {
|
||||
for (final File file : new File("plugins").listFiles()) {
|
||||
if (file.getName().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate whether the version number is marked showing that it should not be updated by this program
|
||||
*/
|
||||
/* private boolean hasTag(String version) {
|
||||
for (final String string : BukkitUpdater.NO_UPDATE_TAG) {
|
||||
if (version.contains(string)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} */
|
||||
|
||||
public boolean read() {
|
||||
try {
|
||||
final URLConnection conn = this.url.openConnection();
|
||||
@@ -331,10 +209,9 @@ public class BukkitUpdater extends Updater {
|
||||
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
final String response = reader.readLine();
|
||||
final JSONArray array = response != null ? (JSONArray) JSONValue.parse(response) : null;
|
||||
|
||||
final JSONArray array = (JSONArray) JSONValue.parse(response);
|
||||
|
||||
if (array.size() == 0) {
|
||||
if (array == null || array.size() == 0) {
|
||||
this.plugin.getLogger().warning("The updater could not find any files for the project id " + this.id);
|
||||
this.result = UpdateResult.FAIL_BADID;
|
||||
return false;
|
||||
|
||||
@@ -97,9 +97,7 @@ public final class SpigotUpdater extends Updater {
|
||||
private static final byte[] API_KEY = ("key=98BE0FE67F88AB82B4C197FAF1DC3B69206EFDCC4D3B80FC83A00037510B99B4&resource=" + ID).getBytes(Charsets.UTF_8);
|
||||
|
||||
public String getSpigotVersion() throws IOException {
|
||||
Closer closer = Closer.create();
|
||||
|
||||
try {
|
||||
try (Closer closer = Closer.create()) {
|
||||
HttpURLConnection con = (HttpURLConnection) new URL(API_URL).openConnection();
|
||||
con.setDoOutput(true);
|
||||
con.setRequestMethod(ACTION);
|
||||
@@ -108,8 +106,6 @@ public final class SpigotUpdater extends Updater {
|
||||
InputStreamReader isr = closer.register(new InputStreamReader(con.getInputStream()));
|
||||
BufferedReader br = closer.register(new BufferedReader(isr));
|
||||
return br.readLine();
|
||||
} finally {
|
||||
closer.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
name: ProtocolLib
|
||||
version: ${project.fullVersion}
|
||||
#version: ${project.fullVersion}
|
||||
version: 4.4.0
|
||||
description: Provides read/write access to the Minecraft protocol.
|
||||
authors: [dmulloy2, comphenix]
|
||||
|
||||
|
||||
+47
-24
@@ -1,48 +1,66 @@
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.minecraft.server.v1_11_R1.DispenserRegistry;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_11_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_11_R1.inventory.CraftItemFactory;
|
||||
import org.bukkit.craftbukkit.v1_11_R1.util.Versioning;
|
||||
|
||||
import com.comphenix.protocol.utility.Constants;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
import net.minecraft.server.v1_13_R2.DispenserRegistry;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_13_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemFactory;
|
||||
import org.bukkit.craftbukkit.v1_13_R2.util.Versioning;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Used to ensure that ProtocolLib and Bukkit is prepared to be tested.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class BukkitInitialization {
|
||||
private static boolean initialized;
|
||||
private static boolean packaged;
|
||||
private static final BukkitInitialization instance = new BukkitInitialization();
|
||||
|
||||
private BukkitInitialization() {
|
||||
System.out.println("Created new BukkitInitialization on " + Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
private boolean initialized;
|
||||
private boolean packaged;
|
||||
|
||||
public static synchronized void initializePackage() {
|
||||
instance.setPackage();
|
||||
}
|
||||
|
||||
public static synchronized void initializeItemMeta() {
|
||||
instance.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Bukkit and ProtocolLib such that we can perfrom unit testing.
|
||||
* @throws IllegalAccessException If we are unable to initialize Bukkit.
|
||||
* Initialize Bukkit and ProtocolLib such that we can perfrom unit testing
|
||||
*/
|
||||
public static void initializeItemMeta() throws IllegalAccessException {
|
||||
private void initialize() {
|
||||
if (!initialized) {
|
||||
// Denote that we're done
|
||||
initialized = true;
|
||||
|
||||
initializePackage();
|
||||
|
||||
try {
|
||||
LogManager.getLogger();
|
||||
} catch (Throwable ex) {
|
||||
// Happens only on my Jenkins, but if it errors here it works when it matters
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
DispenserRegistry.c(); // Basically registers everything
|
||||
|
||||
// Mock the server object
|
||||
Server mockedServer = mock(Server.class);
|
||||
|
||||
when(mockedServer.getLogger()).thenReturn(Logger.getLogger("Minecraft"));
|
||||
when(mockedServer.getLogger()).thenReturn(java.util.logging.Logger.getLogger("Minecraft"));
|
||||
when(mockedServer.getName()).thenReturn("Mock Server");
|
||||
when(mockedServer.getVersion()).thenReturn(CraftServer.class.getPackage().getImplementationVersion());
|
||||
when(mockedServer.getBukkitVersion()).thenReturn(Versioning.getBukkitVersion());
|
||||
@@ -52,20 +70,25 @@ public class BukkitInitialization {
|
||||
|
||||
// Inject this fake server
|
||||
Bukkit.setServer(mockedServer);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that package names are correctly set up.
|
||||
*/
|
||||
public static void initializePackage() {
|
||||
private void setPackage() {
|
||||
if (!packaged) {
|
||||
packaged = true;
|
||||
|
||||
try {
|
||||
LogManager.getLogger();
|
||||
} catch (Throwable ex) {
|
||||
// Happens only on my Jenkins, but if it errors here it works when it matters
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
MinecraftReflection.setMinecraftPackage(Constants.NMS, Constants.OBC);
|
||||
MinecraftVersion.setCurrentVersion(MinecraftVersion.FROSTBURN_UPDATE);
|
||||
MinecraftVersion.setCurrentVersion(MinecraftVersion.AQUATIC_UPDATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
public class MinecraftVersionTest {
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
/**
|
||||
* 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
|
||||
* 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 net.minecraft.server.v1_11_R1.PacketLoginInStart;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
|
||||
import net.minecraft.server.v1_13_R2.EnumProtocol;
|
||||
import net.minecraft.server.v1_13_R2.EnumProtocolDirection;
|
||||
import net.minecraft.server.v1_13_R2.PacketLoginInStart;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
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 static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author dmulloy2
|
||||
@@ -54,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);
|
||||
}
|
||||
}
|
||||
+69
-91
@@ -16,33 +16,32 @@
|
||||
*/
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
import net.minecraft.server.v1_11_R1.AttributeModifier;
|
||||
import net.minecraft.server.v1_11_R1.DataWatcher;
|
||||
import net.minecraft.server.v1_11_R1.Entity;
|
||||
import net.minecraft.server.v1_11_R1.EntityLightning;
|
||||
import net.minecraft.server.v1_11_R1.MobEffect;
|
||||
import net.minecraft.server.v1_11_R1.MobEffectList;
|
||||
import net.minecraft.server.v1_11_R1.PacketPlayOutBoss;
|
||||
import net.minecraft.server.v1_11_R1.PacketPlayOutUpdateAttributes;
|
||||
import net.minecraft.server.v1_11_R1.PacketPlayOutUpdateAttributes.AttributeSnapshot;
|
||||
import com.comphenix.protocol.BukkitInitialization;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.PacketConstructor;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.Util;
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.*;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.minecraft.server.v1_13_R2.*;
|
||||
import net.minecraft.server.v1_13_R2.PacketPlayOutUpdateAttributes.AttributeSnapshot;
|
||||
|
||||
import org.apache.commons.lang.SerializationUtils;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.DyeColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.WorldType;
|
||||
@@ -55,27 +54,8 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
|
||||
import com.comphenix.protocol.BukkitInitialization;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.injector.PacketConstructor;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.Util;
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
|
||||
import com.comphenix.protocol.wrappers.WrappedBlockData;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
import com.google.common.collect.Lists;
|
||||
import static com.comphenix.protocol.utility.TestUtils.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
// Ensure that the CraftItemFactory is mockable
|
||||
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
|
||||
@@ -87,7 +67,7 @@ public class PacketContainerTest {
|
||||
private EquivalentConverter<ItemStack> itemConvert = BukkitConverters.getItemStackConverter();
|
||||
|
||||
@BeforeClass
|
||||
public static void initializeBukkit() throws IllegalAccessException {
|
||||
public static void initializeBukkit() {
|
||||
BukkitInitialization.initializeItemMeta();
|
||||
BukkitInitialization.initializePackage();
|
||||
}
|
||||
@@ -177,20 +157,20 @@ public class PacketContainerTest {
|
||||
|
||||
@Test
|
||||
public void testGetStringArrays() {
|
||||
PacketContainer tabComplete = new PacketContainer(PacketType.Play.Server.TAB_COMPLETE);
|
||||
testObjectArray(tabComplete.getStringArrays(), 0, new String[0], new String[] { "hello", "world" });
|
||||
PacketContainer packet = new PacketContainer(PacketType.Play.Client.UPDATE_SIGN);
|
||||
testObjectArray(packet.getStringArrays(), 0, new String[0], new String[] { "hello", "world" });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIntegerArrays() {
|
||||
// Contains a byte array we will test
|
||||
PacketContainer mapChunkBulk = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
|
||||
StructureModifier<int[]> integers = mapChunkBulk.getIntegerArrays();
|
||||
PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY);
|
||||
StructureModifier<int[]> integers = packet.getIntegerArrays();
|
||||
int[] testArray = new int[] { 1, 2, 3 };
|
||||
|
||||
// Pre and post conditions
|
||||
assertArrayEquals(null, integers.read(0));
|
||||
mapChunkBulk.getModifier().writeDefaults();
|
||||
packet.getModifier().writeDefaults();
|
||||
assertArrayEquals(new int[0], integers.read(0));
|
||||
|
||||
integers.write(0, testArray);
|
||||
@@ -212,7 +192,7 @@ public class PacketContainerTest {
|
||||
}
|
||||
|
||||
private ItemStack itemWithData() {
|
||||
ItemStack item = new ItemStack(Material.WOOL, 1, DyeColor.GREEN.getWoolData());
|
||||
ItemStack item = new ItemStack(Material.GREEN_WOOL, 1);
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
meta.setDisplayName(ChatColor.GREEN + "Green Wool");
|
||||
meta.setLore(Util.asList(ChatColor.WHITE + "This is lore."));
|
||||
@@ -236,17 +216,7 @@ public class PacketContainerTest {
|
||||
|
||||
// Read back array
|
||||
List<ItemStack> comparison = itemAccess.read(0);
|
||||
assertEquals(items, comparison);
|
||||
}
|
||||
|
||||
private boolean equivalentItem(ItemStack first, ItemStack second) {
|
||||
if (first == null) {
|
||||
return second == null;
|
||||
} else if (second == null) {
|
||||
return false;
|
||||
} else {
|
||||
return first.getType().equals(second.getType());
|
||||
}
|
||||
assertItemCollectionsEqual(items, comparison);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -288,14 +258,11 @@ public class PacketContainerTest {
|
||||
PacketContainer mobSpawnPacket = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY_LIVING);
|
||||
StructureModifier<WrappedDataWatcher> watcherAccessor = mobSpawnPacket.getDataWatcherModifier();
|
||||
|
||||
Entity entity = new EntityLightning(null, 0, 0, 0, true);
|
||||
DataWatcher watcher = entity.getDataWatcher();
|
||||
|
||||
WrappedDataWatcher dataWatcher = new WrappedDataWatcher(watcher);
|
||||
dataWatcher.setObject(1, (byte) 1);
|
||||
dataWatcher.setObject(2, 301);
|
||||
dataWatcher.setObject(3, true);
|
||||
dataWatcher.setObject(4, "Lorem");
|
||||
WrappedDataWatcher dataWatcher = new WrappedDataWatcher();
|
||||
dataWatcher.setObject(new WrappedDataWatcherObject(1, Registry.get(Byte.class)), (byte) 1);
|
||||
dataWatcher.setObject(new WrappedDataWatcherObject(2, Registry.get(String.class)), "Lorem");
|
||||
dataWatcher.setObject(new WrappedDataWatcherObject(3, Registry.get(Boolean.class)), true);
|
||||
dataWatcher.setObject(new WrappedDataWatcherObject(4, Registry.getUUIDSerializer(true)), Optional.of(UUID.randomUUID()));
|
||||
|
||||
assertNull(watcherAccessor.read(0));
|
||||
|
||||
@@ -463,6 +430,7 @@ public class PacketContainerTest {
|
||||
int e = 0;
|
||||
if (effect.isAmbient()) e |= 1;
|
||||
if (effect.hasParticles()) e |= 2;
|
||||
if (mobEffect.f()) e |= 4;
|
||||
|
||||
assertEquals(e, (byte) packet.getBytes().read(2));
|
||||
}
|
||||
@@ -480,7 +448,7 @@ public class PacketContainerTest {
|
||||
PacketContainer container = new PacketContainer(PacketType.Play.Server.NAMED_SOUND_EFFECT);
|
||||
container.getSoundCategories().write(0, SoundCategory.PLAYERS);
|
||||
|
||||
assertEquals(container.getSoundCategories().read(0), SoundCategory.PLAYERS);
|
||||
assertEquals(SoundCategory.PLAYERS, container.getSoundCategories().read(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -499,17 +467,24 @@ public class PacketContainerTest {
|
||||
assertEquals(container.getEnumModifier(Action.class, PacketPlayOutBoss.Action.class).read(0), Action.UPDATE_PCT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDimensionManager() {
|
||||
PacketContainer container = new PacketContainer(PacketType.Play.Server.RESPAWN);
|
||||
container.getDimensions().write(0, 1);
|
||||
assertEquals((Object) 1, container.getDimensions().read(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions from the outbound Boss packet. Used for testing generic enums.
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public static enum Action {
|
||||
public enum Action {
|
||||
ADD,
|
||||
REMOVE,
|
||||
UPDATE_PCT,
|
||||
UPDATE_NAME,
|
||||
UPDATE_STYLE,
|
||||
UPDATE_PROPERTIES;
|
||||
UPDATE_PROPERTIES
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -526,15 +501,14 @@ public class PacketContainerTest {
|
||||
}
|
||||
|
||||
private static final List<PacketType> BLACKLISTED = Util.asList(
|
||||
PacketType.Play.Client.CUSTOM_PAYLOAD, PacketType.Play.Server.CUSTOM_PAYLOAD,
|
||||
PacketType.Play.Server.SET_COOLDOWN
|
||||
PacketType.Play.Server.TAGS
|
||||
);
|
||||
|
||||
@Test
|
||||
public void testDeepClone() {
|
||||
// Try constructing all the packets
|
||||
for (PacketType type : PacketType.values()) {
|
||||
if (BLACKLISTED.contains(type) || type.isDeprecated()) {
|
||||
if (BLACKLISTED.contains(type) || type.isDeprecated() || type.name().contains("CUSTOM_PAYLOAD")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -545,7 +519,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
|
||||
@@ -554,8 +528,10 @@ public class PacketContainerTest {
|
||||
// Make sure watchable collections can be cloned
|
||||
if (type == PacketType.Play.Server.ENTITY_METADATA) {
|
||||
constructed.getWatchableCollectionModifier().write(0, Util.asList(
|
||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Byte.class)), (byte) 1),
|
||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(String.class)), "String"),
|
||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Byte.class)),
|
||||
(byte) 1),
|
||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(String.class)),
|
||||
"String"),
|
||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Float.class)), 1.0F)
|
||||
));
|
||||
}
|
||||
@@ -578,24 +554,12 @@ public class PacketContainerTest {
|
||||
testEquality(firstMod.read(i), secondMod.read(i));
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (!registered) {
|
||||
e.printStackTrace();
|
||||
} else {
|
||||
// Something is very wrong
|
||||
throw e;
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
throw new RuntimeException("Failed to serialize packet " + type, e);
|
||||
} catch (Exception 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) {
|
||||
@@ -608,12 +572,17 @@ public class PacketContainerTest {
|
||||
}
|
||||
}
|
||||
|
||||
if (a instanceof ItemStack || b instanceof ItemStack) {
|
||||
assertItemsEqual((ItemStack) a, (ItemStack) b);
|
||||
return;
|
||||
}
|
||||
|
||||
if (a == null || b == null) {
|
||||
if (a == null && b == null) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (a.equals(b) || Objects.equals(a, b) || a.toString().equals(b.toString())) {
|
||||
if (a.equals(b) || Objects.equals(a, b) || stringEquality(a, b)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -625,6 +594,15 @@ public class PacketContainerTest {
|
||||
assertEquals(a, b);
|
||||
}
|
||||
|
||||
private boolean stringEquality(Object a, Object b) {
|
||||
try {
|
||||
return a.toString().equals(b.toString());
|
||||
} catch (Exception ex) {
|
||||
// internal null pointers, usually
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying array as an object array.
|
||||
* @param val - array wrapped as an Object.
|
||||
|
||||
@@ -13,6 +13,7 @@ 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;
|
||||
|
||||
@@ -29,6 +30,8 @@ public class WirePacketTest {
|
||||
@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);
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
package com.comphenix.protocol.reflect.cloning;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
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_11_R1.ItemStack;
|
||||
import net.minecraft.server.v1_11_R1.NonNullList;
|
||||
import net.minecraft.server.v1_13_R2.ItemStack;
|
||||
import net.minecraft.server.v1_13_R2.NonNullList;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
+2
-1
@@ -15,7 +15,8 @@ public class MinecraftMethodsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendPacketMethod() {
|
||||
public void testSendPacketMethods() {
|
||||
assertNotNull(MinecraftMethods.getSendPacketMethod());
|
||||
assertNotNull(MinecraftMethods.getNetworkManagerHandleMethod());
|
||||
}
|
||||
}
|
||||
+156
-146
@@ -1,146 +1,156 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import net.minecraft.server.v1_11_R1.ChatComponentText;
|
||||
import net.minecraft.server.v1_11_R1.ChunkCoordIntPair;
|
||||
import net.minecraft.server.v1_11_R1.DataWatcher;
|
||||
import net.minecraft.server.v1_11_R1.IBlockData;
|
||||
import net.minecraft.server.v1_11_R1.IChatBaseComponent;
|
||||
import net.minecraft.server.v1_11_R1.IChatBaseComponent.ChatSerializer;
|
||||
import net.minecraft.server.v1_11_R1.NBTCompressedStreamTools;
|
||||
import net.minecraft.server.v1_11_R1.PacketPlayOutUpdateAttributes.AttributeSnapshot;
|
||||
import net.minecraft.server.v1_11_R1.PlayerConnection;
|
||||
import net.minecraft.server.v1_11_R1.ServerPing;
|
||||
import net.minecraft.server.v1_11_R1.ServerPing.ServerData;
|
||||
import net.minecraft.server.v1_11_R1.ServerPing.ServerPingPlayerSample;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
|
||||
import com.comphenix.protocol.BukkitInitialization;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
|
||||
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
|
||||
public class MinecraftReflectionTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void initializeBukkit() throws IllegalAccessException {
|
||||
BukkitInitialization.initializeItemMeta();
|
||||
}
|
||||
|
||||
// Mocking objects
|
||||
private interface FakeEntity {
|
||||
public Entity getBukkitEntity();
|
||||
}
|
||||
|
||||
private interface FakeBlock {
|
||||
public Block getBukkitEntity();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void undoMocking() {
|
||||
// NOP
|
||||
MinecraftReflection.minecraftPackage = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBukkitMethod() {
|
||||
FakeEntity entity = mock(FakeEntity.class);
|
||||
FakeBlock block = mock(FakeBlock.class);
|
||||
|
||||
MinecraftReflection.getBukkitEntity(entity);
|
||||
MinecraftReflection.getBukkitEntity(block);
|
||||
|
||||
verify(entity, times(1)).getBukkitEntity();
|
||||
verify(block, times(1)).getBukkitEntity();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testIllegalClass() {
|
||||
MinecraftReflection.getBukkitEntity("Hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttributeSnapshot() {
|
||||
assertEquals(AttributeSnapshot.class, MinecraftReflection.getAttributeSnapshotClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChatComponent() {
|
||||
assertEquals(IChatBaseComponent.class, MinecraftReflection.getIChatBaseComponentClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChatComponentText() {
|
||||
assertEquals(ChatComponentText.class, MinecraftReflection.getChatComponentTextClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChatSerializer() {
|
||||
assertEquals(ChatSerializer.class, MinecraftReflection.getChatSerializerClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChunkCoordIntPair() {
|
||||
assertEquals(ChunkCoordIntPair.class, MinecraftReflection.getChunkCoordIntPair());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIBlockData() {
|
||||
assertEquals(IBlockData.class, MinecraftReflection.getIBlockDataClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayerConnection() {
|
||||
assertEquals(PlayerConnection.class, MinecraftReflection.getPlayerConnectionClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerPing() {
|
||||
assertEquals(ServerPing.class, MinecraftReflection.getServerPingClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerPingPlayerSample() {
|
||||
assertEquals(ServerPingPlayerSample.class, MinecraftReflection.getServerPingPlayerSampleClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerPingServerData() {
|
||||
assertEquals(ServerData.class, MinecraftReflection.getServerPingServerDataClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNbtStreamTools() {
|
||||
assertEquals(NBTCompressedStreamTools.class, MinecraftReflection.getNbtCompressedStreamToolsClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataWatcherItem() {
|
||||
assertEquals(DataWatcher.Item.class, MinecraftReflection.getDataWatcherItemClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItemStacks() {
|
||||
ItemStack stack = new ItemStack(Material.GOLD_SWORD);
|
||||
Object nmsStack = MinecraftReflection.getMinecraftItemStack(stack);
|
||||
assertEquals(stack, MinecraftReflection.getBukkitItemStack(nmsStack));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGameProfile() {
|
||||
assertEquals(GameProfile.class, MinecraftReflection.getGameProfileClass());
|
||||
}
|
||||
}
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import net.minecraft.server.v1_13_R2.ChatComponentText;
|
||||
import net.minecraft.server.v1_13_R2.ChunkCoordIntPair;
|
||||
import net.minecraft.server.v1_13_R2.DataWatcher;
|
||||
import net.minecraft.server.v1_13_R2.IBlockData;
|
||||
import net.minecraft.server.v1_13_R2.IChatBaseComponent;
|
||||
import net.minecraft.server.v1_13_R2.IChatBaseComponent.ChatSerializer;
|
||||
import net.minecraft.server.v1_13_R2.NBTCompressedStreamTools;
|
||||
import net.minecraft.server.v1_13_R2.PacketPlayOutUpdateAttributes.AttributeSnapshot;
|
||||
import net.minecraft.server.v1_13_R2.PlayerConnection;
|
||||
import net.minecraft.server.v1_13_R2.ServerPing;
|
||||
import net.minecraft.server.v1_13_R2.ServerPing.ServerData;
|
||||
import net.minecraft.server.v1_13_R2.ServerPing.ServerPingPlayerSample;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
|
||||
import com.comphenix.protocol.BukkitInitialization;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
|
||||
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
|
||||
public class MinecraftReflectionTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void initializeBukkit() {
|
||||
BukkitInitialization.initializeItemMeta();
|
||||
}
|
||||
|
||||
// Mocking objects
|
||||
private interface FakeEntity {
|
||||
Entity getBukkitEntity();
|
||||
}
|
||||
|
||||
private interface FakeBlock {
|
||||
Block getBukkitEntity();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void undoMocking() {
|
||||
// NOP
|
||||
MinecraftReflection.minecraftPackage = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBukkitMethod() {
|
||||
FakeEntity entity = mock(FakeEntity.class);
|
||||
FakeBlock block = mock(FakeBlock.class);
|
||||
|
||||
MinecraftReflection.getBukkitEntity(entity);
|
||||
MinecraftReflection.getBukkitEntity(block);
|
||||
|
||||
verify(entity, times(1)).getBukkitEntity();
|
||||
verify(block, times(1)).getBukkitEntity();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testIllegalClass() {
|
||||
MinecraftReflection.getBukkitEntity("Hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullable() {
|
||||
assertNull(MinecraftReflection.getNullableNMS("ProtocolLib"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttributeSnapshot() {
|
||||
assertEquals(AttributeSnapshot.class, MinecraftReflection.getAttributeSnapshotClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChatComponent() {
|
||||
assertEquals(IChatBaseComponent.class, MinecraftReflection.getIChatBaseComponentClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChatComponentText() {
|
||||
assertEquals(ChatComponentText.class, MinecraftReflection.getChatComponentTextClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChatSerializer() {
|
||||
assertEquals(ChatSerializer.class, MinecraftReflection.getChatSerializerClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChunkCoordIntPair() {
|
||||
assertEquals(ChunkCoordIntPair.class, MinecraftReflection.getChunkCoordIntPair());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIBlockData() {
|
||||
assertEquals(IBlockData.class, MinecraftReflection.getIBlockDataClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayerConnection() {
|
||||
assertEquals(PlayerConnection.class, MinecraftReflection.getPlayerConnectionClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerPing() {
|
||||
assertEquals(ServerPing.class, MinecraftReflection.getServerPingClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerPingPlayerSample() {
|
||||
assertEquals(ServerPingPlayerSample.class, MinecraftReflection.getServerPingPlayerSampleClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerPingServerData() {
|
||||
assertEquals(ServerData.class, MinecraftReflection.getServerPingServerDataClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNbtStreamTools() {
|
||||
assertEquals(NBTCompressedStreamTools.class, MinecraftReflection.getNbtCompressedStreamToolsClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataWatcherItem() {
|
||||
assertEquals(DataWatcher.Item.class, MinecraftReflection.getDataWatcherItemClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItemStacks() {
|
||||
ItemStack stack = new ItemStack(Material.GOLDEN_SWORD);
|
||||
Object nmsStack = MinecraftReflection.getMinecraftItemStack(stack);
|
||||
assertItemsEqual(stack, MinecraftReflection.getBukkitItemStack(nmsStack));
|
||||
|
||||
// The NMS handle for CraftItemStack is null with Material.AIR, make sure it is handled correctly
|
||||
assertNotNull(MinecraftReflection.getMinecraftItemStack(CraftItemStack.asCraftCopy(new ItemStack(Material.AIR))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGameProfile() {
|
||||
assertEquals(GameProfile.class, MinecraftReflection.getGameProfileClass());
|
||||
}
|
||||
}
|
||||
+11
-15
@@ -1,17 +1,14 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import java.io.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import com.comphenix.protocol.BukkitInitialization;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
|
||||
import net.minecraft.server.v1_11_R1.IntHashMap;
|
||||
import net.minecraft.server.v1_13_R2.IntHashMap;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.DyeColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
@@ -20,9 +17,8 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
|
||||
import com.comphenix.protocol.BukkitInitialization;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
|
||||
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
|
||||
@@ -30,7 +26,7 @@ import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
public class StreamSerializerTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void initializeBukkit() throws IllegalAccessException {
|
||||
public static void initializeBukkit() {
|
||||
BukkitInitialization.initializeItemMeta();
|
||||
}
|
||||
|
||||
@@ -80,13 +76,13 @@ public class StreamSerializerTest {
|
||||
String serialized = serializer.serializeItemStack(initial);
|
||||
ItemStack deserialized = serializer.deserializeItemStack(serialized);
|
||||
|
||||
assertEquals(initial, deserialized);
|
||||
assertItemsEqual(initial, deserialized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItemMeta() throws IOException {
|
||||
StreamSerializer serializer = new StreamSerializer();
|
||||
ItemStack initial = new ItemStack(Material.WOOL, 2, DyeColor.BLUE.getWoolData());
|
||||
ItemStack initial = new ItemStack(Material.BLUE_WOOL, 2);
|
||||
|
||||
ItemMeta meta = initial.getItemMeta();
|
||||
meta.setDisplayName(ChatColor.BLUE + "Blue Wool");
|
||||
@@ -95,6 +91,6 @@ public class StreamSerializerTest {
|
||||
String serialized = serializer.serializeItemStack(initial);
|
||||
ItemStack deserialized = serializer.deserializeItemStack(serialized);
|
||||
|
||||
assertEquals(initial, deserialized);
|
||||
assertItemsEqual(initial, deserialized);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
public static void assertItemCollectionsEqual(List<ItemStack> first, List<ItemStack> second) {
|
||||
assertEquals(first.size(), second.size());
|
||||
for (int i = 0; i < first.size(); i++) {
|
||||
assertItemsEqual(first.get(i), second.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertItemsEqual(ItemStack first, ItemStack second) {
|
||||
if (first == null) {
|
||||
assertNull(second);
|
||||
} else {
|
||||
assertNotNull(first);
|
||||
|
||||
// The legacy check in ItemStack#isSimilar causes a null pointer
|
||||
assertEquals(first.getType(), second.getType());
|
||||
assertEquals(first.getDurability(), second.getDurability());
|
||||
assertEquals(first.hasItemMeta(), second.hasItemMeta());
|
||||
if (first.hasItemMeta()) {
|
||||
assertTrue(Bukkit.getItemFactory().equals(first.getItemMeta(), second.getItemMeta()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean equivalentItem(ItemStack first, ItemStack second) {
|
||||
if (first == null) {
|
||||
return second == null;
|
||||
} else if (second == null) {
|
||||
return false;
|
||||
} else {
|
||||
return first.getType().equals(second.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import com.comphenix.protocol.BukkitInitialization;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class BukkitConvertersTest {
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
BukkitInitialization.initializeItemMeta();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItemStacks() {
|
||||
ItemStack item = new ItemStack(Material.DIAMOND_SWORD, 16);
|
||||
item.addEnchantment(Enchantment.DAMAGE_ALL, 4);
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
meta.setDisplayName(ChatColor.GREEN + "Diamond Sword");
|
||||
item.setItemMeta(meta);
|
||||
|
||||
EquivalentConverter<ItemStack> converter = BukkitConverters.getItemStackConverter();
|
||||
Object nmsStack = converter.getGeneric(item);
|
||||
ItemStack back = converter.getSpecific(nmsStack);
|
||||
|
||||
assertEquals(item.getType(), back.getType());
|
||||
assertEquals(item.getDurability(), back.getDurability());
|
||||
assertEquals(item.hasItemMeta(), back.hasItemMeta());
|
||||
assertTrue(Bukkit.getItemFactory().equals(item.getItemMeta(), back.getItemMeta()));
|
||||
}
|
||||
}
|
||||
Loaded 100 of 114 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user