Compare commits

...
61 Commits
Author SHA1 Message Date
Dan Mulloy 41bb4bacb2 Update version to 4.8.0 for release 2022-03-04 16:28:13 -05:00
Pasqual Koschmieder 9487c42985 Update to 1.18.2 (#1521) 2022-02-28 13:36:20 -05:00
derklaro 55f7b67f9d Update to junit 5, remove powermock 2022-02-25 00:11:26 -05:00
Pasqual Koschmieder e44d1e6051 Improve auto wrapper handling (#1518) 2022-02-25 00:02:11 -05:00
Pasqual Koschmieder baecaf4ca4 Remove unsafe reflection trying to access the modifiers field (#1517) 2022-02-24 23:56:22 -05:00
Pasqual Koschmieder 151d4a289f Prevent memory leaks with loads of PacketMarker objects (#1511)
closes #1509
2022-02-24 16:41:43 -05:00
Pasqual Koschmieder 1b4d79b302 Fix compile issues with the latest spigot version (#1510) 2022-02-24 16:18:56 -05:00
Pasqual Koschmieder 74833f8680 Correctly resolve protocol version of player (#1473) 2022-01-02 12:32:46 -06:00
Pasqual Koschmieder e77f8ced4c Update to 1.18.1 (#1461)
No changes required except for registering the protocol version
2021-12-19 20:50:22 -05:00
Dan Mulloy 1cd83e493d Update version to 4.8.0-SNAPSHOT 2021-11-30 14:14:50 -05:00
Pasqual Koschmieder 40b6c66491 Update to minecraft 1.18 (#1446) 2021-11-30 14:10:03 -05:00
LewUwU 723fc2c7ec Update NettyVersion.java (#1438)
The issue was caused due to a change in the pom of netty (for dependencies)

(#Generated by netty-parent/pom.xml
#Mon, 11 Oct 2021 13:22:16 +0200

netty-all.version=4.1.69.Final
netty-all.buildDate=2021-10-11 13\:22\:16 +0200
netty-all.commitDate=2021-10-11 09\:21\:07 +0000
netty-all.shortCommitHash=34a31522f0
netty-all.longCommitHash=34a31522f0145e2d434aaea2ef8ac5ed8d1a91a0
netty-all.repoStatus=dirty)

Is not more netty-common, now is netty-all
2021-11-14 12:09:34 -05:00
Dan Mulloy dd687ce175 Fix NPE in PlayerInfoData equals
Fixes #1400
Closes #1106
2021-11-14 00:41:20 -05:00
Nassim Jahnke 8774d87d59 Add 1.17 long jump pose (#1409) 2021-11-13 23:51:37 -05:00
Dan Mulloy 8361cf078f Fix GH actions build 2021-09-22 13:29:23 -04:00
Rothes dbedab0c14 Check entityID whether is negative or not. (#1362) 2021-09-22 10:51:47 -04:00
Pim van der Loos 5dda8c8ab1 Fix entity tracker methods for 1.17 (#1354)
- In 1.17, EntityTrackerEntries use ServerPlayerConnections instead of EntityPlayers as they did before. This caused the updateEntity to silently fail when removing the players from the trackedPlayers collection (of connections). This was resolved by retrieving the connections of the players before removing them from the list on 1.17+.
The getEntityTrackers method failed because it could not find any players for the same reason. This was resolved by retrieving the player from the connection before retrieving the Bukkit player from the EntityPlayer object when running on 1.17+.
- This fixes dmulloy2/ProtocolLib#1340
2021-07-26 23:51:24 -07:00
Dan Mulloy 4c0c18d7c6 Remove stack trace for list converter
Addresses #1331
2021-07-10 11:09:56 -04:00
Dan Mulloy 9c20455bf6 Update version to 4.7.1-SNAPSHOT 2021-07-10 11:08:35 -04:00
Dan Mulloy 8175443da0 Update to 4.7.0 2021-07-09 17:16:07 -04:00
Dan Mulloy 95a884974a Only set chat UUID if not already set
Fixes #1310
2021-07-08 23:33:17 -04:00
Dan Mulloy 153dd61994 Update to 1.17.1
Fixes #1315
2021-07-08 23:22:56 -04:00
Dan Mulloy 263ec8a36e Add FLYING packet back for backwards compat
Fixes #1275
2021-06-27 18:13:40 -04:00
Julian af46ba4d1a Fix support for Minecraft versions under the bee update (#1276) 2021-06-27 17:57:14 -04:00
Dan Mulloy dd85904642 Metrics: only track each plugin once 2021-06-26 21:22:52 -04:00
Dan Mulloy 7a8fce224e Set mc object regex in 1.17
Fixes #1271
2021-06-26 20:57:39 -04:00
Dan Mulloy 9ca7c91a76 Simplify packet registry 2021-06-24 17:14:00 -04:00
Dan Mulloy 466354cd2c Cache list constructors to save on exceptions 2021-06-24 17:13:40 -04:00
Dan Mulloy 99504dab8f Abstract out structure modifiers to allow internal structures to be read 2021-06-24 17:13:05 -04:00
Dan Mulloy 9b6603e2eb Use fuzzy reflection for effect types 2021-06-21 16:10:58 -04:00
Dan Mulloy 4fc476a125 Use right int 2 object map class for Paper
Addresses #1217
2021-06-20 12:42:41 -04:00
Dan Mulloy 583ed4b58a Update scoreboard team class
Addresses #1232
2021-06-20 12:35:03 -04:00
Dan Mulloy 638e81b9ce Merge branch 'master' of https://github.com/dmulloy2/ProtocolLib 2021-06-20 12:08:52 -04:00
Dan Mulloy 9a0703d05d Fix attribute builder in 1.17
Addresses #1224
2021-06-20 12:08:47 -04:00
Pasqual Koschmieder 9de096f783 Fix sdk command for jitpack builds (#1241) 2021-06-20 11:51:55 -04:00
Dan Mulloy c54a99945d Try fixing some of the unit tests 2021-06-19 22:45:17 -04:00
Pasqual Koschmieder 90a38cc15c Restore backwards compatibility (#1235)
* Remove usages of net.minecraft and craftbukkit
* Restore packet type backward compatibility (tested on 1.8)
* Re-add last removed packets
* Fix sub class naming for newer minecraft versions
2021-06-19 22:20:54 -04:00
Dan Mulloy 76930ae6e8 Remove travis
It's gonna be a lot of work to build, so we're just gonna use GitHub actions
2021-06-19 22:15:03 -04:00
Pasqual Koschmieder d745cfb184 Switch to GH-Workflow & use java 16 on jitpack builds (#1236)
* Add github actions workflow
* Add jitpack.yml to configure java version
2021-06-19 22:11:12 -04:00
Dan Mulloy 4be2bf38ff Switch from travis-ci.org to .com 2021-06-18 10:53:17 -04:00
Pasqual Koschmieder 0a32f24f08 Fix EntityUseAction & Hand read for minecraft 1.17 (#1230) 2021-06-18 10:47:48 -04:00
Dan Mulloy 1c2bc274dd Fix differently mapped fastutil classes in Paper
Fixes #1228
2021-06-16 18:22:53 -04:00
Dan Mulloy 9b54794f6b Fix cloning in 1.17
Addresses #1222
2021-06-15 23:58:40 -04:00
Dan Mulloy 42bec5a858 Fix entity trackers in 1.17
Addresses #1217
2021-06-15 18:39:34 -04:00
Dan Mulloy fa317c1167 Working on live server 2021-06-14 16:36:22 -04:00
Dan Mulloy cc17b9ee6e Finish up unit tests 2021-06-13 17:18:36 -04:00
Dan Mulloy 190ca1ff6a Almost there, /theoretically/ 2021-06-13 15:57:23 -04:00
Dan Mulloy c51930121f Fix a bunch more classpaths
Also worked on fixing some wrappers
2021-06-13 11:36:44 -04:00
Dan Mulloy c7a8d734d4 Fix some more class paths 2021-06-12 23:53:09 -04:00
Dan Mulloy b446cf2183 Update unit tests to 1.17, fix packet lookups 2021-06-12 15:28:01 -04:00
Dan Mulloy f11c246276 Deprecate SocketInjector#getSocket (see #1188) 2021-06-04 18:24:30 -04:00
Camotoy 6f91bd23de Remove inferences of SocketChannel presence in temporary player (#1188)
To note: this is yet another compatibility change for my Geyser work, but https://github.com/PaperMC/Paper/pull/5611 will also break without these changes as Unix domain sockets don't implement SocketChannel.

The temporary player method delegation directed the isOnline and getName methods to functions that require the channel to be an instance of SocketChannel, when this won't always be the case. To solve this, this PR redirects `getSocket().getRemoteSocketAddress()` to `injector.getAddress()` which returns the same value. To determine if the player is online, a new method is created in SocketInjector to determine if a connection is online (which also returns the same value as before this commit).
2021-06-04 18:21:48 -04:00
Pim van der Loos 45c293df7d Update ByteBuddy dependency (#1156)
- Updated ByteBuddy dependency to add support for Java 17 (introduced in 1.10.21) and to take advantage of any fixes and improvements made between versions.
2021-05-29 13:01:45 -04:00
Camotoy 5acdb2b3c5 Mark connectionHandler as sharable (#1179)
Fixes #1170.

The reasoning for this PR is largely outlined in the above issue. As the ChannelInboundHandlerAdapter has no private class, a race condition cannot occur and therefore marking the class as sharable should incur no cost.
2021-05-29 13:00:58 -04:00
Camotoy 72c1f3e26c Redirect #mcdevs link to libera.chat (#1181)
The #mcdevs IRC channel is in the process of migrating away from Freenode.
2021-05-29 12:59:26 -04:00
Minecrell b3ccf82597 WrappedServerPing: Properly translate MotD to components for RGB colors (#1152)
In previous Minecraft versions, using WrappedServerPing.setMotD(String)
behaved exactly like using Bukkit's ServerListPingEvent.setMotd(String).

With the addition of RGB colors in Minecraft 1.16, Spigot's
ServerListPingEvent was patched to translate the MotD string to the
chat component equivalent to make it possible to use RGB colors in MotDs.
In general, using raw legacy color codes (e.g. §c) within a (JSON) text
component tends to cause weird issues on newer Minecraft versions,
so it's better to translate them to the JSON equivalents on the server.

However, the WrappedServerPing implementation in ProtocolLib was never
updated with the same change, which makes it behave differently from
Spigot's ServerListPingEvent now. Using ServerListPingEvent RGB color
codes work, using ProtocolLib they do not work.

To fix this, this commit changes WrappedServerPing.setMotD(String) to
use the same method as Spigot for translating the legacy text to the
JSON/chat component equivalent.

This allows for example ServerListPlus to use Spigot's RGB color codes
(e.g. &x&7&9&b&8&f&fHello) without requiring any changes in ServerListPlus.
2021-05-02 17:43:32 -04:00
LewUwU 0c01a11755 Fixed protocol injection with latest netty on minecraft 1.11 and below (#1067) 2021-05-01 15:51:52 -04:00
Pim van der Loos 7ce3f471bf Add support for Java 16 (#1120)
Switched from the now-unavailable ClassLoader::defineClass method to the java.lang.invoke.MethodHandles.Lookup::defineClass. This is available on Java 9+.
2021-04-04 23:03:05 -04:00
Dan Mulloy 97972acee8 Add converter for game state ids
Fixes #1041
2021-02-27 15:38:05 -05:00
LOOHP a0bb11e1bd Add support for PaperMC's new Adventure API Component (#1103) 2021-02-27 14:15:43 -05:00
Dan Mulloy ba74fceed6 Update version to 4.6.1-SNAPSHOT 2021-02-27 14:15:01 -05:00
120 changed files with 5924 additions and 3575 deletions

No files matched your search

+2 -2
View File
@@ -19,7 +19,7 @@ This page will detail specific things that must be done if you intend to contrib
#### Ready? Let's get started!
1. Read the [Protocol Changes](http://wiki.vg/Protocol_History). Always make sure the list is both
complete and correct. If you're unsure, don't hesitate to ask in #mcdevs (the people who maintain wiki.vg) on [freenode.net](http://freenode.net)
complete and correct. If you're unsure, don't hesitate to ask in #mcdevs (the people who maintain wiki.vg) on [libera.chat](http://libera.chat)
or #spigot on [irc.spi.gt](http://irc.spi.gt) ([webchat](https://irc.spi.gt/iris/?channels=spigot)).
2. Search for usages of the now-defunct NMS package guard and change them.
3. The class `com.comphenix.protocol.PacketType` contains a list of all the packets. If any packets were added or removed
@@ -35,4 +35,4 @@ and the release date.
8. `com.comphenix.protocol.utility.MinecraftProtocolVersion` contains a map of all the protocol version integers.
If the protocol version has been incremented, add a new line to the map.
9. `mvn` in root directory again. If it builds successfully, test on the appropriate version of a Spigot server. If
the build fails, debug!
the build fails, debug!
+25
View File
@@ -0,0 +1,25 @@
name: ProtocolLib full build lifecycle
on:
- push
- pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup java
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '17'
- name: Run maven build lifecycle
run: mvn --batch-mode clean test package
- name: Upload plugin file
uses: actions/upload-artifact@v2
with:
name: ProtocolLib
path: target/ProtocolLib.jar
+2 -1
View File
@@ -179,4 +179,5 @@ logs/
.gradle/
build/
logs/
logs/
.java-version
-11
View File
@@ -1,11 +0,0 @@
language: java
jdk:
- openjdk8
script: mvn clean test
install: true
notifications:
email:
recipients:
- "dmulloy2@live.com"
on_success: change
on_failure: always
+3 -3
View File
@@ -1,4 +1,4 @@
# ProtocolLib [![Travis Status](https://travis-ci.org/dmulloy2/ProtocolLib.svg?branch=master)](https://travis-ci.org/dmulloy2/ProtocolLib)
# ProtocolLib
Certain tasks are impossible to perform with the standard Bukkit API, and may require
working with and even modify Minecraft directly. A common technique is to modify incoming
@@ -53,7 +53,7 @@ You can also add ProtocolLib as a Maven dependency:
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>4.6.0</version>
<version>4.7.0</version>
</dependency>
</dependencies>
````
@@ -66,7 +66,7 @@ repositories {
}
dependencies {
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.6.0";
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.7.0";
}
```
+5
View File
@@ -0,0 +1,5 @@
before_install:
- source "$HOME/.sdkman/bin/sdkman-init.sh"
- sdk update
- sdk install java 17.0.1-tem
- sdk use java 17.0.1-tem
+81 -35
View File
@@ -1,10 +1,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.comphenix.protocol</groupId>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>4.6.0</version>
<version>4.8.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>https://github.com/dmulloy2/ProtocolLib</url>
@@ -16,8 +16,10 @@
<project.build.number></project.build.number>
<project.fullVersion>${project.version}</project.fullVersion>
<powermock.version>2.0.7</powermock.version>
<spigot.version>1.16.4-R0.1-SNAPSHOT</spigot.version>
<junit.version>5.8.2</junit.version>
<mockito.version>4.3.1</mockito.version>
<netty.version>4.1.74.Final</netty.version>
<spigot.version>1.18.2-R0.1-SNAPSHOT</spigot.version>
</properties>
<build>
@@ -71,7 +73,7 @@
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<version>3.10.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@@ -81,7 +83,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<version>3.2.2</version>
<configuration>
<archive>
<manifestEntries>
@@ -99,6 +101,14 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
</dependency>
</dependencies>
<configuration>
<trimStackTrace>false</trimStackTrace>
<useFile>false</useFile>
@@ -108,19 +118,22 @@
<value>${project.version}</value>
</property>
</systemProperties>
<!-- We need this as we use reflection in tests to ensure equality -->
<argLine>--add-opens java.base/java.util=ALL-UNNAMED</argLine>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.2</version>
<configuration>
<failOnError>false</failOnError>
<encoding>ISO-8859-1</encoding>
<doctitle>ProtocolLib JavaDocs</doctitle>
<windowtitle>ProtocolLib JavaDocs</windowtitle>
<bottom>Copyright &#169; {inceptionYear}&#x2013;{currentYear}
Comphenix and dmulloy2. Licensed under the GNU GPL v2.</bottom>
Comphenix and dmulloy2. Licensed under the GNU GPL v2.
</bottom>
<additionalOptions>
<additionalOption>-Xdoclint:none</additionalOption>
</additionalOptions>
@@ -147,8 +160,8 @@
</activation>
<properties>
<project.build.number>-b${env.BUILD_NUMBER}</project.build.number>
<!--<project.fullVersion>${project.version}${project.build.number}</project.fullVersion>-->
<project.fullVersion>${project.version}</project.fullVersion>
<project.fullVersion>${project.version}${project.build.number}</project.fullVersion>
<!--<project.fullVersion>${project.version}</project.fullVersion>-->
</properties>
</profile>
@@ -182,7 +195,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.2</version>
<executions>
<execution>
<id>attach-javadocs</id>
@@ -224,7 +237,7 @@
<licenses>
<license>
<name>GNU GENERAL PUBLIC LICENSE - Version 2, June 1991</name>
<url>http://www.gnu.org/licenses/gpl-2.0.txt</url>
<url>https://www.gnu.org/licenses/gpl-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
@@ -233,7 +246,7 @@
<developer>
<id>dmulloy2</id>
<name>Dan Mulloy</name>
<url>http://dmulloy2.net/</url>
<url>https://dmulloy2.net/</url>
<roles>
<role>developer</role>
<role>maintainer</role>
@@ -243,7 +256,7 @@
<id>aadnk</id>
<name>Kristian S. Stangeland</name>
<email>kr_stang@hotmail.com</email>
<url>http://comphenix.net/</url>
<url>https://comphenix.net/</url>
<roles>
<role>former author</role>
</roles>
@@ -254,18 +267,18 @@
<distributionManagement>
<repository>
<id>dmulloy2-releases</id>
<url>https://repo.dmulloy2.net/nexus/repository/releases/</url>
<url>https://repo.dmulloy2.net/repository/releases/</url>
</repository>
<snapshotRepository>
<id>dmulloy2-snapshots</id>
<url>https://repo.dmulloy2.net/nexus/repository/snapshots/</url>
<url>https://repo.dmulloy2.net/repository/snapshots/</url>
</snapshotRepository>
</distributionManagement>
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/nexus/repository/public/</url>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
<repository>
<id>spigot-repo</id>
@@ -274,55 +287,88 @@
</repositories>
<dependencies>
<!-- Compile with the old version of Netty for backwards compat -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
<scope>provided</scope>
</dependency>
<!-- core minecraft dependencies -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Compile with the old version of Netty for backwards compat -->
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/net.kyori/adventure-text-serializer-gson -->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-gson</artifactId>
<version>4.9.3</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy -->
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.10.16</version>
<version>1.12.8</version>
</dependency>
<!-- Testing dependencies -->
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.5.10</version>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.netty/netty-common -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
<version>${netty.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.netty/netty-transport -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>${netty.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -19,6 +19,7 @@ package com.comphenix.protocol;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -31,13 +32,16 @@ import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Charsets;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.libs.org.apache.commons.io.HexDump;
import org.bukkit.plugin.Plugin;
/**
@@ -47,6 +51,8 @@ import org.bukkit.plugin.Plugin;
public class PacketLogging implements CommandExecutor, PacketListener {
public static final String NAME = "packetlog";
private static MethodAccessor HEX_DUMP;
private List<PacketType> sendingTypes = new ArrayList<>();
private List<PacketType> receivingTypes = new ArrayList<>();
@@ -199,7 +205,13 @@ public class PacketLogging implements CommandExecutor, PacketListener {
private static String hexDump(byte[] bytes) throws IOException {
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
HexDump.dump(bytes, 0, output, 0);
if (HEX_DUMP == null) {
Class<?> hexDumpClass = MinecraftReflection.getLibraryClass("org.apache.commons.io.HexDump");
HEX_DUMP = Accessors.getMethodAccessor(FuzzyReflection.fromClass(hexDumpClass)
.getMethodByParameters("dump", byte[].class, long.class, OutputStream.class, int.class));
}
HEX_DUMP.invoke(null, bytes, 0, output, 0);
return new String(output.toByteArray(), Charsets.UTF_8);
}
}
@@ -11,6 +11,7 @@ import java.util.function.Consumer;
import com.comphenix.protocol.PacketTypeLookup.ClassLookup;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.Constants;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Preconditions;
@@ -107,93 +108,105 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType SPAWN_ENTITY_LIVING = new PacketType(PROTOCOL, SENDER, 0x02, "SpawnEntityLiving", "SPacketSpawnMob");
public static final PacketType SPAWN_ENTITY_PAINTING = new PacketType(PROTOCOL, SENDER, 0x03, "SpawnEntityPainting", "SPacketSpawnPainting");
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x04, "NamedEntitySpawn", "SPacketSpawnPlayer");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x05, "Animation", "SPacketAnimation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x06, "Statistic", "SPacketStatistics");
public static final PacketType BLOCK_BREAK = new PacketType(PROTOCOL, SENDER, 0x07, "BlockBreak");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x08, "BlockBreakAnimation", "SPacketBlockBreakAnim");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x09, "TileEntityData", "SPacketUpdateTileEntity");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x0A, "BlockAction", "SPacketBlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0B, "BlockChange", "SPacketBlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0C, "Boss", "SPacketUpdateBossInfo");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0D, "ServerDifficulty", "SPacketServerDifficulty");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x0E, "Chat", "SPacketChat");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0F, "TabComplete", "SPacketTabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x10, "Commands");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x11, "Transaction", "SPacketConfirmTransaction");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x12, "CloseWindow", "SPacketCloseWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x13, "WindowItems", "SPacketWindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x14, "WindowData", "SPacketWindowProperty");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x15, "SetSlot", "SPacketSetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x16, "SetCooldown", "SPacketCooldown");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x17, "CustomPayload", "SPacketCustomPayload");
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x18, "CustomSoundEffect", "SPacketCustomSound");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x19, "KickDisconnect", "SPacketDisconnect");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1A, "EntityStatus", "SPacketEntityStatus");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1B, "Explosion", "SPacketExplosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1C, "UnloadChunk", "SPacketUnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1D, "GameStateChange", "SPacketChangeGameState");
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x1E, "OpenWindowHorse");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x1F, "KeepAlive", "SPacketKeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x20, "MapChunk", "SPacketChunkData");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x21, "WorldEvent", "SPacketEffect");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x22, "WorldParticles", "SPacketParticles");
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x23, "LightUpdate");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x24, "Login", "SPacketJoinGame");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x25, "Map", "SPacketMaps");
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x26, "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x27, "Entity$RelEntityMove", "SPacketEntity$S15PacketEntityRelMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x28, "Entity$RelEntityMoveLook", "SPacketEntity$S17PacketEntityLookMove");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x29, "Entity$EntityLook", "SPacketEntity$S16PacketEntityLook");
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 0x2A, "Entity", "SPacketEntity");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2B, "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x2C, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x2D, "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2E, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x2F, "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x30, "Abilities", "SPacketPlayerAbilities");
public static final PacketType COMBAT_EVENT = new PacketType(PROTOCOL, SENDER, 0x31, "CombatEvent", "SPacketCombatEvent");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x32, "PlayerInfo", "SPacketPlayerListItem");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x33, "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x34, "Position");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x35, "Recipes", "SPacketRecipeBook");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x36, "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x37, "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x38, "ResourcePackSend", "SPacketResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x39, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x3A, "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x3B, "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x3C, "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType WORLD_BORDER = new PacketType(PROTOCOL, SENDER, 0x3D, "WorldBorder", "SPacketWorldBorder");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x3E, "Camera", "SPacketCamera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x3F, "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x40, "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x41, "ViewDistance");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x42, "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x43, "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x44, "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x45, "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x46, "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x47, "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x48, "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x49, "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x4A, "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x4B, "Mount", "SPacketSetPassengers");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x4C, "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x4D, "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x4E, "UpdateTime", "SPacketTimeUpdate");
public static final PacketType TITLE = new PacketType(PROTOCOL, SENDER, 0x4F, "Title", "SPacketTitle");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x50, "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x51, "NamedSoundEffect");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x52, "StopSound");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x53, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x54, "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x55, "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x56, "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x57, "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x58, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x59, "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x5A, "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x5B, "Tags");
public static final PacketType ADD_VIBRATION_SIGNAL = new PacketType(PROTOCOL, SENDER, 0x05, "AddVibrationSignal");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, "Animation", "SPacketAnimation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x07, "Statistic", "SPacketStatistics");
public static final PacketType BLOCK_BREAK = new PacketType(PROTOCOL, SENDER, 0x08, "BlockBreak");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x09, "BlockBreakAnimation", "SPacketBlockBreakAnim");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x0A, "TileEntityData", "SPacketUpdateTileEntity");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x0B, "BlockAction", "SPacketBlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0C, "BlockChange", "SPacketBlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0D, "Boss", "SPacketUpdateBossInfo");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0E, "ServerDifficulty", "SPacketServerDifficulty");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x0F, "Chat", "SPacketChat");
public static final PacketType CLEAR_TITLES = new PacketType(PROTOCOL, SENDER, 0x10, "ClearTitles");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x11, "TabComplete", "SPacketTabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x12, "Commands");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x13, "CloseWindow", "SPacketCloseWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x14, "WindowItems", "SPacketWindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x15, "WindowData", "SPacketWindowProperty");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x16, "SetSlot", "SPacketSetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x17, "SetCooldown", "SPacketCooldown");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x18, "CustomPayload", "SPacketCustomPayload");
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x19, "CustomSoundEffect", "SPacketCustomSound");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1A, "KickDisconnect", "SPacketDisconnect");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1B, "EntityStatus", "SPacketEntityStatus");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1C, "Explosion", "SPacketExplosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1D, "UnloadChunk", "SPacketUnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1E, "GameStateChange", "SPacketChangeGameState");
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x1F, "OpenWindowHorse");
public static final PacketType INITIALIZE_BORDER = new PacketType(PROTOCOL, SENDER, 0x20, "InitializeBorder");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x21, "KeepAlive", "SPacketKeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x22, "MapChunk", "SPacketChunkData", "LevelChunkWithLight");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x23, "WorldEvent", "SPacketEffect");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x24, "WorldParticles", "SPacketParticles");
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x25, "LightUpdate");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x26, "Login", "SPacketJoinGame");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x27, "Map", "SPacketMaps");
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x28, "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x29, "Entity$PacketPlayOutRelEntityMove", "Entity$RelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x2A, "Entity$PacketPlayOutRelEntityMoveLook", "Entity$RelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x2B, "Entity$PacketPlayOutEntityLook", "Entity$EntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2C, "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x2D, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x2E, "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2F, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x30, "Ping");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x31, "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x32, "Abilities", "SPacketPlayerAbilities");
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x33, "PlayerCombatEnd");
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x34, "PlayerCombatEnter");
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x35, "PlayerCombatKill");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x36, "PlayerInfo", "SPacketPlayerListItem");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x37, "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x38, "Position");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x39, "Recipes", "SPacketRecipeBook");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x3A, "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x3B, "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x3C, "ResourcePackSend", "SPacketResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x3D, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x3E, "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x3F, "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x40, "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x41, "SetActionBarText");
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x42, "SetBorderCenter");
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x43, "SetBorderLerpSize");
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x44, "SetBorderSize");
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x45, "SetBorderWarningDelay");
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x46, "SetBorderWarningDistance");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x47, "Camera", "SPacketCamera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x48, "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x49, "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x4A, "ViewDistance");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x4B, "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x4C, "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x4D, "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x4E, "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x4F, "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x50, "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x51, "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x52, "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x53, "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x54, "Mount", "SPacketSetPassengers");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x55, "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x56, "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x57, "SetSimulationDistance");
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x58, "SetSubtitleText");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x59, "UpdateTime", "SPacketTimeUpdate");
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5A, "SetTitleText");
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x5B, "SetTitlesAnimation");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x5C, "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x5D, "NamedSoundEffect");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x5E, "StopSound");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x5F, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x60, "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x61, "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x62, "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x63, "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x64, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x65, "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x66, "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x67, "Tags");
// ---- Removed in 1.9
@@ -256,7 +269,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @deprecated Removed in 1.14
*/
@Deprecated
public static final PacketType BED = new PacketType(PROTOCOL, SENDER, 0x33, "Bed", "SPacketUseBed");
public static final PacketType BED = new PacketType(PROTOCOL, SENDER, 251, "Bed", "SPacketUseBed");
/**
* @deprecated Renamed to {@link #BED}
@@ -268,7 +281,37 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @deprecated Removed in 1.16
*/
@Deprecated
public static final PacketType SPAWN_ENTITY_WEATHER = new PacketType(PROTOCOL, SENDER, 0x02, "SpawnEntityWeather", "SPacketSpawnGlobalEntity");
public static final PacketType SPAWN_ENTITY_WEATHER = new PacketType(PROTOCOL, SENDER, 250, "SpawnEntityWeather", "SPacketSpawnGlobalEntity");
/**
* @deprecated Removed in 1.17, split into separate packets
*/
@Deprecated
public static final PacketType TITLE = new PacketType(PROTOCOL, SENDER, 249, "Title");
/**
* @deprecated Removed in 1.17, split into separate packets
*/
@Deprecated
public static final PacketType WORLD_BORDER = new PacketType(PROTOCOL, SENDER, 248, "WorldBorder");
/**
* @deprecated Removed in 1.17, split into separate packets
*/
@Deprecated
public static final PacketType COMBAT_EVENT = new PacketType(PROTOCOL, SENDER, 247, "CombatEvent");
/**
* @deprecated Removed in 1.17
*/
@Deprecated
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 246, "Transaction", "SPacketConfirmTransaction");
/**
* @deprecated Made abstract in 1.17, no actual packet anymore
*/
@Deprecated
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 245, "Entity", "SPacketEntity");
private final static Server INSTANCE = new Server();
@@ -297,29 +340,29 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x04, "ClientCommand", "CPacketClientStatus");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x05, "Settings", "CPacketClientSettings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x06, "TabComplete", "CPacketTabComplete");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x07, "Transaction", "CPacketConfirmTransaction");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x08, "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x09, "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0A, "CloseWindow", "CPacketCloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0B, "CustomPayload", "CPacketCustomPayload");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x0C, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0D, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0E, "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x0F, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x10, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x11, "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x12, "Flying$Position", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x13, "Flying$PositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x14, "Flying$Look", "CPacketPlayer$Rotation");
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 0x15, "Flying", "CPacketPlayer");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x16, "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x17, "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x18, "PickItem");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x19, "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x1A, "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x1B, "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x1C, "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1D, "SteerVehicle", "CPacketInput");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x07, "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x08, "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x09, "CloseWindow", "CPacketCloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0A, "CustomPayload", "CPacketCustomPayload");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x0B, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0C, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0D, "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x0E, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x0F, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x10, "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x11, "Flying$PacketPlayInPosition", "Flying$Position", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x12, "Flying$PacketPlayInPositionLook", "Flying$PositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x13, "Flying$PacketPlayInLook", "Flying$Look", "CPacketPlayer$Rotation");
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x14, "Flying$d");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x15, "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x16, "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x17, "PickItem");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x18, "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x19, "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x1A, "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x1B, "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1C, "SteerVehicle", "CPacketInput");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x1D, "Pong", "ServerboundPongPacket");
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x1E, "RecipeSettings");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x1F, "RecipeDisplayed", "CPacketRecipeInfo");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x20, "ItemName");
@@ -339,6 +382,18 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x2E, "UseItem", "CPacketPlayerTryUseItemOnBlock");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x2F, "BlockPlace", "CPacketPlayerTryUseItem");
/**
* @deprecated Removed in 1.17
*/
@Deprecated
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 255, "Transaction", "CPacketConfirmTransaction");
/**
* @deprecated Removed in 1.17
*/
@Deprecated
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 254, "Flying", "CPacketPlayer");
private final static Client INSTANCE = new Client();
// Prevent accidental construction
@@ -490,15 +545,23 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @author Kristian
*/
public enum Protocol {
HANDSHAKING,
PLAY,
STATUS,
LOGIN,
HANDSHAKING("Handshaking", "handshake"),
PLAY("Play", "game"),
STATUS("Status", "status"),
LOGIN("Login", "login"),
/**
* Only for packets removed in Minecraft 1.7.2
*/
LEGACY;
LEGACY("", "");
private String packetName;
private String mojangName;
Protocol(String packetName, String mojangName) {
this.packetName = packetName;
this.mojangName = mojangName;
}
/**
* Retrieve the correct protocol enum from a given vanilla enum instance.
@@ -520,7 +583,11 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
}
public String getPacketName() {
return WordUtils.capitalize(name().toLowerCase(Locale.ENGLISH));
return packetName;
}
public String getMojangName() {
return mojangName;
}
public String getMcpPacketName() {
@@ -537,12 +604,22 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
/**
* Indicates that packets of this type will be sent by connected clients.
*/
CLIENT,
CLIENT("Serverbound", "In", "client"),
/**
* Indicate that packets of this type will be sent by the current server.
*/
SERVER;
SERVER("Clientbound", "Out", "server");
private String mojangName;
private String packetName;
private String mcpName;
Sender(String mojangName, String packetName, String mcpName) {
this.mojangName = mojangName;
this.packetName = packetName;
this.mcpName = mcpName;
}
/**
* Retrieve the equivialent connection side.
@@ -553,11 +630,15 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
}
public String getPacketName() {
return this == CLIENT ? "In" : "Out";
return packetName;
}
public String getMcpPacketName() {
return name().toLowerCase(Locale.ENGLISH);
return mcpName;
}
public String getMojangName() {
return mojangName;
}
}
@@ -574,13 +655,13 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
/**
* Protocol version of all the current IDs.
*/
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.NETHER_UPDATE;
private static final MinecraftVersion PROTOCOL_VERSION = Constants.CURRENT_VERSION;
private final Protocol protocol;
private final Sender sender;
private final int currentId;
private final MinecraftVersion version;
private final String[] classNames;
private final List<String> classNames;
String[] names;
private String name;
@@ -710,7 +791,17 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
}
}
private static String formatMojangClassName(Protocol protocol, Sender sender, String name) {
return "net.minecraft.network.protocol." + protocol.getMojangName() + "." + sender.getMojangName()
+ name + "Packet";
}
private static String formatClassName(Protocol protocol, Sender sender, String name) {
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
return "net.minecraft.network.protocol." + protocol.getMojangName() + ".Packet"
+ protocol.getPacketName() + sender.getPacketName() + name;
}
String base = MinecraftReflection.getMinecraftPackage() + ".Packet";
if (name.startsWith(base)) {
return name;
@@ -810,8 +901,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
// Check any aliases
for (PacketType check : map.values()) {
String[] aliases = check.getClassNames();
if (aliases.length > 1) {
List<String> aliases = check.getClassNames();
if (aliases.size() > 1) {
for (String alias : aliases) {
if (alias.equals(clazz)) {
// We have a match!
@@ -852,7 +943,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* Determine if a given class represents a packet class.
* @param packetClass - the class to lookup.
* @return TRUE if this is a packet class, FALSE otherwise.
* @deprecated Doesn't really have a purpose
*/
@Deprecated
public static boolean hasClass(Class<?> packetClass) {
return PacketRegistry.getPacketType(packetClass) != null;
}
@@ -931,12 +1024,13 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
this.currentId = currentId;
this.version = version;
this.classNames = new String[names.length];
for (int i = 0; i < classNames.length; i++) {
this.classNames = new ArrayList<>();
for (int i = 0; i < names.length; i++) {
if (isMcpPacketName(names[i])) { // Minecraft MCP packets
classNames[i] = formatMcpClassName(protocol, sender, names[i]);
classNames.add(formatMcpClassName(protocol, sender, names[i]));
} else {
classNames[i] = formatClassName(protocol, sender, names[i]);
classNames.add(formatClassName(protocol, sender, names[i]));
classNames.add(formatMojangClassName(protocol, sender, names[i]));
}
}
@@ -997,7 +1091,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
return currentId;
}
public String[] getClassNames() {
public List<String> getClassNames() {
return classNames;
}
@@ -1006,11 +1100,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @return The packet class, or NULL if not found.
*/
public Class<?> getPacketClass() {
try {
return PacketRegistry.getPacketClassFromType(this);
} catch (Exception e) {
return null;
}
return PacketRegistry.tryGetPacketClass(this).orElse(null);
}
// Only used by Enum processor
@@ -1112,7 +1202,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
Class<?> clazz = getPacketClass();
if (clazz == null)
return name() + "[" + protocol + ", " + sender + ", " + currentId + ", classNames: " + Arrays.toString(classNames) + " (unregistered)]";
return name() + "[" + protocol + ", " + sender + ", " + currentId + ", classNames: " + classNames + " (unregistered)]";
else
return name() + "[class=" + clazz.getSimpleName() + ", id=" + currentId + "]";
}
@@ -110,22 +110,6 @@ public class PacketTypeEnum implements Iterable<PacketType> {
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
*/
@Deprecated
public PacketType valueOf(String name) {
for (PacketType member : members) {
if (member.name().equals(name))
return member;
}
return null;
}
/**
* Retrieve every registered member.
@@ -108,7 +108,7 @@ class PacketTypeLookup {
// Skip unknown current packets
if (type.getCurrentId() != PacketType.UNKNOWN_PACKET) {
idLookup.getMap(type.getProtocol(), type.getSender()).put(type.getCurrentId(), type);
classLookup.getMap(type.getProtocol(), type.getSender()).put(type.getClassNames()[0], type);
classLookup.getMap(type.getProtocol(), type.getSender()).put(type.getClassNames().get(0), type);
}
nameLookup.put(type.name(), type);
}
@@ -38,6 +38,7 @@ import com.comphenix.protocol.updater.Updater;
import com.comphenix.protocol.updater.Updater.UpdateType;
import com.comphenix.protocol.utility.ChatExtensions;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.NettyVersion;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
@@ -170,7 +171,11 @@ public class ProtocolLib extends JavaPlugin {
// Print the state of the debug mode
if (config.isDebug()) {
logger.warning("Debug mode is enabled!");
logger.info("Detected netty version: " + NettyVersion.getVersion());
} else {
NettyVersion.getVersion(); // this will cache the version
}
// And the state of the error reporter
if (config.isDetailedErrorReporting()) {
detailedReporter.setDetailedReporting(true);
@@ -38,12 +38,12 @@ public class ProtocolLibrary {
/**
* The maximum version ProtocolLib has been tested with.
*/
public static final String MAXIMUM_MINECRAFT_VERSION = "1.16.5";
public static final String MAXIMUM_MINECRAFT_VERSION = "1.18.2";
/**
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.16.5) was released.
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.18.2) was released.
*/
public static final String MINECRAFT_LAST_RELEASE_DATE = "2021-01-15";
public static final String MINECRAFT_LAST_RELEASE_DATE = "2022-02-28";
/**
* Plugins that are currently incompatible with ProtocolLib.
@@ -20,6 +20,7 @@ import java.text.MessageFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.comphenix.protocol.utility.MinecraftVersion;
import org.bukkit.plugin.Plugin;
/**
@@ -82,4 +83,10 @@ public class ProtocolLogger {
logger.log(Level.WARNING, "[Debug] " + message, ex);
}
}
public static void warnAbove(MinecraftVersion version, String message, Object... args) {
if (version.atOrAbove()) {
log(Level.WARNING, message, args);
}
}
}
@@ -32,11 +32,11 @@ public class PacketTypeSet {
* @param type - the type to add.
*/
public synchronized void addType(PacketType type) {
Class<?> packetClass = getPacketClass(type);
Class<?> packetClass = type.getPacketClass();
types.add(Preconditions.checkNotNull(type, "type cannot be NULL."));
if (packetClass != null) {
classes.add(getPacketClass(type));
classes.add(type.getPacketClass());
}
}
@@ -55,11 +55,11 @@ public class PacketTypeSet {
* @param type - the type to remove.
*/
public synchronized void removeType(PacketType type) {
Class<?> packetClass = getPacketClass(type);
Class<?> packetClass = type.getPacketClass();
types.remove(Preconditions.checkNotNull(type, "type cannot be NULL."));
if (packetClass != null) {
classes.remove(getPacketClass(type));
classes.remove(packetClass);
}
}
@@ -72,16 +72,7 @@ public class PacketTypeSet {
removeType(type);
}
}
/**
* Retrieve the packet class associated with a particular type.
* @param type - the packet type.
* @return The associated packet type.
*/
protected Class<?> getPacketClass(PacketType type) {
return PacketRegistry.getPacketClassFromType(type);
}
/**
* Determine if the given packet type exists in the set.
* @param type - the type to find.
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,45 @@
package com.comphenix.protocol.events;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.wrappers.Converters;
import java.util.List;
import java.util.Optional;
public class InternalStructure extends AbstractStructure {
protected InternalStructure(Object handle, StructureModifier<Object> structureModifier) {
super(handle, structureModifier);
}
protected static final EquivalentConverter<InternalStructure> CONVERTER = new EquivalentConverter<InternalStructure>() {
@Override
public Object getGeneric(InternalStructure specific) {
return specific.handle;
}
@Override
public InternalStructure getSpecific(Object generic) {
return new InternalStructure(generic, new StructureModifier<>(generic.getClass()).withTarget(generic));
}
@Override
public Class<InternalStructure> getSpecificType() {
return InternalStructure.class;
}
};
public StructureModifier<InternalStructure> getStructures() {
return structureModifier.withType(Object.class, CONVERTER);
}
public StructureModifier<Optional<InternalStructure>> getOptionalStructures() {
return structureModifier.withType(Optional.class, Converters.optional(CONVERTER));
}
@Override
public String toString() {
return "InternalStructure[handle=" + handle + " (" + handle.getClass().getSimpleName() + ")]";
}
}
File diff suppressed because it is too large. Load diff
@@ -45,6 +45,7 @@ import net.bytebuddy.matcher.ElementMatchers;
import org.bukkit.*;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.profile.PlayerProfile;
import com.comphenix.protocol.utility.ByteBuddyFactory;
@@ -176,7 +177,12 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
public UUID getUniqueId() {
return uuid;
}
@Override
public PlayerProfile getPlayerProfile() {
return null;
}
@Override
public String getName() {
return name;
@@ -17,6 +17,7 @@
package com.comphenix.protocol.injector;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -27,6 +28,7 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftFields;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.WrappedIntHashMap;
@@ -66,7 +68,10 @@ class EntityUtilities {
Collection<?> trackedPlayers = getTrackedPlayers(entity);
List<Object> nmsPlayers = unwrapBukkit(observers);
trackedPlayers.removeAll(nmsPlayers);
List<Object> removingEntries = MinecraftVersion.CAVES_CLIFFS_1.atOrAbove() ?
getPlayerConnections(nmsPlayers) : nmsPlayers;
trackedPlayers.removeAll(removingEntries);
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
@@ -102,7 +107,9 @@ class EntityUtilities {
// Wrap every player - we also ensure that the underlying tracker list is immutable
for (Object tracker : trackedPlayers) {
if (MinecraftReflection.isMinecraftPlayer(tracker)) {
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove() && MinecraftReflection.isServerHandler(tracker)) {
result.add(MinecraftReflection.getBukkitPlayerFromConnection(tracker));
} else if (MinecraftReflection.isMinecraftPlayer(tracker)) {
result.add((Player) MinecraftReflection.getBukkitEntity(tracker));
}
}
@@ -139,7 +146,7 @@ class EntityUtilities {
@SuppressWarnings("unchecked")
private Object getNewEntityTracker(Object worldServer, int entityId) {
if (getChunkProvider == null) {
Class<?> chunkProviderClass = MinecraftReflection.getMinecraftClass("ChunkProviderServer");
Class<?> chunkProviderClass = MinecraftReflection.getChunkProviderServer();
getChunkProvider = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(worldServer.getClass(), false).getMethod(
FuzzyMethodContract.newBuilder().parameterCount(0).returnTypeExact(chunkProviderClass).build()));
@@ -148,7 +155,7 @@ class EntityUtilities {
Object chunkProvider = getChunkProvider.invoke(worldServer);
if (chunkMapField == null) {
Class<?> chunkMapClass = MinecraftReflection.getMinecraftClass("PlayerChunkMap");
Class<?> chunkMapClass = MinecraftReflection.getPlayerChunkMap();
chunkMapField = Accessors.getFieldAccessor(
FuzzyReflection.fromClass(chunkProvider.getClass(), false).getField(
FuzzyFieldContract.newBuilder().typeExact(chunkMapClass).build()));
@@ -157,9 +164,21 @@ class EntityUtilities {
Object playerChunkMap = chunkMapField.get(chunkProvider);
if (trackedEntitiesField == null) {
trackedEntitiesField = Accessors.getFieldAccessor(
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
trackedEntitiesField = Accessors.getFieldAccessor(
FuzzyReflection.fromClass(playerChunkMap.getClass(), true).getField(
FuzzyFieldContract.newBuilder()
.banModifier(Modifier.STATIC)
.requirePublic()
.typeExact(MinecraftReflection.getInt2ObjectMapClass())
.build()
)
);
} else {
trackedEntitiesField = Accessors.getFieldAccessor(
FuzzyReflection.fromClass(playerChunkMap.getClass(), false).getField(
FuzzyFieldContract.newBuilder().typeDerivedOf(Map.class).nameExact("trackedEntities").build()));
}
}
Map<Integer, Object> trackedEntities = (Map<Integer, Object>) trackedEntitiesField.get(playerChunkMap);
@@ -229,14 +248,17 @@ class EntityUtilities {
Object tracker = null;
if (trackerEntry != null) {
// plugins like citizens will use their own tracker
// plugins like citizens will use their own tracker class, so cache the result
FieldAccessor trackerField = trackerFields.computeIfAbsent(trackerEntry.getClass(), x -> {
// get the first entity field
try {
return Accessors.getFieldAccessor(trackerEntry.getClass(), "tracker", true);
} catch (Exception e) {
// Assume it's the first entity field then
return Accessors.getFieldAccessor(FuzzyReflection.fromObject(trackerEntry, true)
.getFieldByType("tracker", MinecraftReflection.getEntityClass()));
return Accessors.getFieldAccessor(FuzzyReflection.fromClass(trackerEntry.getClass(), true)
.getField(FuzzyFieldContract.newBuilder().typeExact(MinecraftReflection.getEntityClass()).build()));
} catch (Exception ex) {
// try with the default class
Class<?> trackerEntryClass = MinecraftReflection.getEntityTrackerClass();
return Accessors.getFieldAccessor(FuzzyReflection.fromClass(trackerEntryClass, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(MinecraftReflection.getEntityClass()).build()));
}
});
@@ -250,6 +272,12 @@ class EntityUtilities {
}
}
private List<Object> getPlayerConnections(List<Object> nmsPlayers) {
List<Object> connections = new ArrayList<>(nmsPlayers.size());
nmsPlayers.forEach(nmsPlayer -> connections.add(MinecraftFields.getPlayerConnection(nmsPlayer)));
return connections;
}
private List<Object> unwrapBukkit(List<Player> players) {
List<Object> output = Lists.newArrayList();
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
@@ -39,14 +39,6 @@ public interface ListenerInvoker {
*/
void invokePacketSending(PacketEvent event);
/**
* Retrieve the associated ID of a packet.
* @param packet - the packet.
* @return The packet ID.
*/
@Deprecated
int getPacketID(Object packet);
/**
* Retrieve the associated type of a packet.
* @param packet - the packet.
@@ -20,6 +20,7 @@ package com.comphenix.protocol.injector;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Optional;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.error.RethrowErrorReporter;
@@ -146,13 +147,11 @@ public class PacketConstructor {
types[i] = Object.class;
}
}
Class<?> packetType = PacketRegistry.getPacketClassFromType(type, true);
if (packetType == null)
throw new IllegalArgumentException("Could not find a packet by the type " + type);
Class<?> packetClass = PacketRegistry.getPacketClassFromType(type);
// Find the correct constructor
for (Constructor<?> constructor : packetType.getConstructors()) {
for (Constructor<?> constructor : packetClass.getConstructors()) {
Class<?>[] params = constructor.getParameterTypes();
if (isCompatible(types, params)) {
@@ -979,12 +979,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
return phaseLoginCount.get();
}
@Override
@Deprecated
public int getPacketID(Object packet) {
return PacketRegistry.getPacketID(packet.getClass());
}
@Override
public PacketType getPacketType(Object packet) {
if (packet == null)
@@ -19,17 +19,29 @@ package com.comphenix.protocol.injector;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.reflect.compiler.CompileListener;
import com.comphenix.protocol.reflect.compiler.CompiledStructureModifier;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.ZeroBuffer;
import io.netty.buffer.ByteBuf;
import com.google.common.base.Preconditions;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;
/**
* Caches structure modifiers.
@@ -37,9 +49,42 @@ import com.comphenix.protocol.utility.MinecraftReflection;
*/
public class StructureCache {
// Structure modifiers
private static ConcurrentMap<PacketType, StructureModifier<Object>> structureModifiers = new ConcurrentHashMap<>();
private static final ConcurrentMap<PacketType, StructureModifier<Object>> structureModifiers = new ConcurrentHashMap<>();
// invocation cache for packets
private static final ConcurrentMap<Class<?>, Supplier<Object>> PACKET_INSTANCE_CREATORS = new ConcurrentHashMap<>();
// packet data serializer which always returns an empty nbt tag compound
private static boolean trickTried;
private static ConstructorAccessor TRICKED_DATA_SERIALIZER;
private static Set<PacketType> compiling = new HashSet<>();
private static final Set<PacketType> compiling = new HashSet<>();
public static Object newPacket(Class<?> clazz) {
Object result = DefaultInstances.DEFAULT.create(clazz);
if (result == null) {
return PACKET_INSTANCE_CREATORS.computeIfAbsent(clazz, $ -> {
ConstructorAccessor accessor = Accessors.getConstructorAccessorOrNull(clazz, MinecraftReflection.getPacketDataSerializerClass());
if (accessor != null) {
return () -> {
try {
return accessor.invoke(MinecraftReflection.getPacketDataSerializer(new ZeroBuffer()));
} catch (Exception exception) {
// try trick nms around as they want a non-null compound in the map_chunk packet constructor
ConstructorAccessor trickyDataSerializerAccessor = getTrickDataSerializerOrNull();
if (trickyDataSerializerAccessor != null) {
return accessor.invoke(trickyDataSerializerAccessor.invoke(new ZeroBuffer()));
}
// the tricks are over
throw new IllegalArgumentException("Unable to create packet " + clazz, exception);
}
};
}
throw new IllegalArgumentException("No matching constructor to create packet in class " + clazz);
}).get();
}
return result;
}
/**
* Creates an empty Minecraft packet of the given type.
@@ -47,20 +92,7 @@ public class StructureCache {
* @return Created packet.
*/
public static Object newPacket(PacketType type) {
Class<?> clazz = PacketRegistry.getPacketClassFromType(type, true);
// Check the return value
if (clazz != null) {
// TODO: Optimize DefaultInstances
Object result = DefaultInstances.DEFAULT.create(clazz);
if (result == null) {
throw new IllegalArgumentException("Failed to create packet for type: " + type);
}
return result;
}
throw new IllegalArgumentException("Cannot find associated packet class: " + type);
return newPacket(PacketRegistry.getPacketClassFromType(type));
}
/**
@@ -91,7 +123,9 @@ public class StructureCache {
*/
public static StructureModifier<Object> getStructure(Class<?> packetType, boolean compile) {
// Get the ID from the class
return getStructure(PacketRegistry.getPacketType(packetType), compile);
PacketType type = PacketRegistry.getPacketType(packetType);
Preconditions.checkNotNull(type, "No packet type associated with " + packetType);
return getStructure(type, compile);
}
/**
@@ -101,13 +135,14 @@ public class StructureCache {
* @return A structure modifier.
*/
public static StructureModifier<Object> getStructure(final PacketType type, boolean compile) {
Preconditions.checkNotNull(type, "type cannot be null");
StructureModifier<Object> result = structureModifiers.get(type);
// We don't want to create this for every lookup
if (result == null) {
// Use the vanilla class definition
final StructureModifier<Object> value = new StructureModifier<Object>(
PacketRegistry.getPacketClassFromType(type, true), MinecraftReflection.getPacketClass(), true);
final StructureModifier<Object> value = new StructureModifier<>(
PacketRegistry.getPacketClassFromType(type), MinecraftReflection.getPacketClass(), true);
result = structureModifiers.putIfAbsent(type, value);
@@ -124,16 +159,42 @@ public class StructureCache {
final BackgroundCompiler compiler = BackgroundCompiler.getInstance();
if (!compiling.contains(type) && compiler != null) {
compiler.scheduleCompilation(result, new CompileListener<Object>() {
@Override
public void onCompiled(StructureModifier<Object> compiledModifier) {
structureModifiers.put(type, compiledModifier);
}
});
compiler.scheduleCompilation(result,
compiledModifier -> structureModifiers.put(type, compiledModifier));
compiling.add(type);
}
}
}
return result;
}
/**
* Creates a packet data serializer sub-class if needed to allow the fixed read of a NbtTagCompound because of a null
* check in the MapChunk packet constructor.
* @return an accessor to a constructor which creates a data serializer.
*/
private static ConstructorAccessor getTrickDataSerializerOrNull() {
if (TRICKED_DATA_SERIALIZER == null && !trickTried) {
// ensure that we only try once to create the class
trickTried = true;
try {
// create an empty instance of a nbt tag compound that we can re-use when needed
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
// create the method in the class to read an empty nbt tag compound (currently used for MAP_CHUNK because of null check)
Class<?> generatedClass = ByteBuddyFactory.getInstance()
.createSubclass(MinecraftReflection.getPacketDataSerializerClass())
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializer")
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
.intercept(FixedValue.value(compound))
.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER = Accessors.getConstructorAccessor(generatedClass, ByteBuf.class);
} catch (Exception ignored) {
// can happen if unsupported
}
}
return TRICKED_DATA_SERIALIZER;
}
}
@@ -30,6 +30,7 @@ import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.ByteBuddyGenerated;
import com.comphenix.protocol.utility.MinecraftFields;
@@ -44,7 +45,6 @@ import com.google.common.base.Preconditions;
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;
@@ -57,6 +57,7 @@ import org.bukkit.entity.Player;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.*;
@@ -85,6 +86,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// Versioning
private static Class<?> PACKET_SET_PROTOCOL = null;
private static FieldAccessor PROTOCOL_VERSION_ACCESSOR = null;
private static AtomicInteger keyId = new AtomicInteger();
private static AttributeKey<Integer> PROTOCOL_KEY;
@@ -273,7 +275,7 @@ 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();
ChannelInjector.this.finalWrite(packet);
}
@Override
@@ -542,7 +544,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
/**
* Invoked when a packet has been written to the channel
*/
private void finalWrite() {
private void finalWrite(Object packet) {
PacketEvent event = finalEvent;
if (event != null) {
@@ -551,6 +553,10 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
currentEvent = null;
processor.invokePostEvent(event, NetworkMarker.getNetworkMarker(event));
// remove the stored packet markers to prevent memory leaks
// See: https://github.com/dmulloy2/ProtocolLib/issues/1509
packetMarker.remove(packet);
}
}
@@ -663,15 +669,21 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
if (PACKET_SET_PROTOCOL == null) {
try {
PACKET_SET_PROTOCOL = PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass();
// resolve the protocol version field here - if we're unable to find it we don't need to worry about it
Field protocolVersionField = FuzzyReflection.fromClass(PACKET_SET_PROTOCOL, true).getField(FuzzyFieldContract.newBuilder()
.banModifier(Modifier.STATIC)
.typeExact(int.class)
.build());
PROTOCOL_VERSION_ACCESSOR = Accessors.getFieldAccessor(protocolVersionField);
} 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);
int protocol = (int) PROTOCOL_VERSION_ACCESSOR.get(packet);
originalChannel.attr(PROTOCOL_KEY).set(protocol);
} catch (Throwable ex) {
// Oh well
@@ -951,7 +963,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
@Override
public Socket getSocket() {
return SocketAdapter.adapt((SocketChannel) injector.originalChannel);
return SocketAdapter.adapt(injector.originalChannel);
}
@Override
@@ -989,6 +1001,11 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
injector.setPlayer(updatedPlayer);
}
@Override
public boolean isConnected() {
return injector.originalChannel.isActive();
}
ChannelInjector getChannelInjector() {
return injector;
}
@@ -54,7 +54,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
private static FieldAccessor READER_INDEX;
private static FieldAccessor WRITER_INDEX;
private static final int CAPACITY = Short.MAX_VALUE;
private static final int CAPACITY = Integer.MAX_VALUE;
private NettyByteBufAdapter(DataInputStream input, DataOutputStream output) {
// Just pick a figure
@@ -1,222 +0,0 @@
/**
* 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 java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.injector.packet.MapContainer;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.Maps;
/**
* @author dmulloy2
*/
public class NettyProtocolRegistry extends ProtocolRegistry {
public NettyProtocolRegistry() {
super();
}
@Override
protected synchronized void initialize() {
if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.BEE_UPDATE)) {
initializeNew();
return;
}
Object[] protocols = enumProtocol.getEnumConstants();
// ID to Packet class maps
Map<Object, Map<Integer, Class<?>>> serverMaps = Maps.newLinkedHashMap();
Map<Object, Map<Integer, Class<?>>> clientMaps = Maps.newLinkedHashMap();
Register result = new Register();
StructureModifier<Object> modifier = null;
// Iterate through the protocols
for (Object protocol : protocols) {
if (modifier == null) {
modifier = new StructureModifier<Object>(protocol.getClass().getSuperclass(), false);
}
StructureModifier<Map<Object, Map<Integer, Class<?>>>> maps = modifier.withTarget(protocol).withType(Map.class);
for (Entry<Object, Map<Integer, Class<?>>> entry : maps.read(0).entrySet()) {
String direction = entry.getKey().toString();
if (direction.contains("CLIENTBOUND")) { // Sent by Server
serverMaps.put(protocol, entry.getValue());
} else if (direction.contains("SERVERBOUND")) { // Sent by Client
clientMaps.put(protocol, entry.getValue());
}
}
}
// Maps we have to occasionally check have changed
for (Object map : serverMaps.values()) {
result.containers.add(new MapContainer(map));
}
for (Object map : clientMaps.values()) {
result.containers.add(new MapContainer(map));
}
for (Object protocol : protocols) {
Enum<?> enumProtocol = (Enum<?>) protocol;
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
// Associate known types
if (serverMaps.containsKey(protocol))
associatePackets(result, serverMaps.get(protocol), equivalent, Sender.SERVER);
if (clientMaps.containsKey(protocol))
associatePackets(result, clientMaps.get(protocol), equivalent, Sender.CLIENT);
}
// Exchange (thread safe, as we have only one writer)
this.register = result;
}
@SuppressWarnings("unchecked")
private synchronized void initializeNew() {
Object[] protocols = enumProtocol.getEnumConstants();
// ID to Packet class maps
Map<Object, Map<Class<?>, Integer>> serverMaps = Maps.newLinkedHashMap();
Map<Object, Map<Class<?>, Integer>> clientMaps = Maps.newLinkedHashMap();
Register result = new Register();
Field mainMapField = null;
Field packetMapField = null;
// Iterate through the protocols
for (Object protocol : protocols) {
if (mainMapField == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(protocol.getClass(), true);
mainMapField = fuzzy.getField(FuzzyFieldContract.newBuilder()
.banModifier(Modifier.STATIC)
.requireModifier(Modifier.FINAL)
.typeDerivedOf(Map.class)
.build());
mainMapField.setAccessible(true);
}
Map<Object, Object> directionMap;
try {
directionMap = (Map<Object, Object>) mainMapField.get(protocol);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to access packet map", ex);
}
for (Entry<Object, Object> entry : directionMap.entrySet()) {
Object holder = entry.getValue();
if (packetMapField == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(holder.getClass(), true);
packetMapField = fuzzy.getField(FuzzyFieldContract.newBuilder()
.banModifier(Modifier.STATIC)
.requireModifier(Modifier.FINAL)
.typeDerivedOf(Map.class)
.build());
packetMapField.setAccessible(true);
}
Map<Class<?>, Integer> packetMap;
try {
packetMap = (Map<Class<?>, Integer>) packetMapField.get(holder);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to access packet map", ex);
}
String direction = entry.getKey().toString();
if (direction.contains("CLIENTBOUND")) { // Sent by Server
serverMaps.put(protocol, packetMap);
} else if (direction.contains("SERVERBOUND")) { // Sent by Client
clientMaps.put(protocol, packetMap);
}
}
}
// Maps we have to occasionally check have changed
// TODO: Find equivalent in Object2IntMap
/* for (Object map : serverMaps.values()) {
result.containers.add(new MapContainer(map));
}
for (Object map : clientMaps.values()) {
result.containers.add(new MapContainer(map));
} */
for (Object protocol : protocols) {
Enum<?> enumProtocol = (Enum<?>) protocol;
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
// Associate known types
if (serverMaps.containsKey(protocol)) {
associatePackets(result, reverse(serverMaps.get(protocol)), equivalent, Sender.SERVER);
}
if (clientMaps.containsKey(protocol)) {
associatePackets(result, reverse(clientMaps.get(protocol)), equivalent, Sender.CLIENT);
}
}
// Exchange (thread safe, as we have only one writer)
this.register = result;
}
/**
* Reverses a key->value map to value->key
* Non-deterministic behavior when multiple keys are mapped to the same value
*/
private <K, V> Map<V, K> reverse(Map<K, V> map) {
Map<V, K> newMap = new HashMap<>(map.size());
for (Entry<K, V> entry : map.entrySet()) {
newMap.put(entry.getValue(), entry.getKey());
}
return newMap;
}
@Override
protected void associatePackets(Register register, Map<Integer, Class<?>> lookup, Protocol protocol, Sender sender) {
for (Entry<Integer, Class<?>> entry : lookup.entrySet()) {
PacketType type = PacketType.fromCurrent(protocol, sender, entry.getKey(), entry.getValue());
// System.out.println(Arrays.toString(type.getClassNames()) + " -> " + entry.getValue());
try {
register.typeToClass.put(type, entry.getValue());
if (sender == Sender.SERVER)
register.serverPackets.add(type);
if (sender == Sender.CLIENT)
register.clientPackets.add(type);
} catch (Exception ex) {
ProtocolLogger.debug("Encountered an exception associating packet " + type, ex);
}
}
}
}
@@ -385,4 +385,4 @@ public class PipelineProxy implements ChannelPipeline {
return pipeline.voidPromise();
}
*/
}
}
@@ -46,6 +46,7 @@ import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.utility.NettyVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.Lists;
@@ -146,10 +147,15 @@ public class ProtocolInjector implements ChannelListener {
protected void initChannel(final Channel channel) throws Exception {
try {
synchronized (networkManagers) {
// For some reason it needs to be delayed on 1.12, but the delay breaks 1.11 and below
// For some reason it needs to be delayed when using netty 4.1.24 (minecraft 1.12) or newer,
// but the delay breaks older minecraft versions
// TODO I see this more as a temporary hotfix than a permanent solution
if (MinecraftVersion.getCurrentVersion().getMinor() >= 12) {
channel.eventLoop().submit(() ->
// Check if the netty version is greater than 4.1.24, that's the version bundled with spigot 1.12
NettyVersion ver = NettyVersion.getVersion();
if ((ver.isValid() && ver.isGreaterThan(4,1,24)) ||
MinecraftVersion.getCurrentVersion().getMinor() >= 12) { // fallback if netty version couldn't be detected
channel.eventLoop().submit(() ->
injectionFactory.fromChannel(channel, ProtocolInjector.this, playerFactory).inject());
} else {
injectionFactory.fromChannel(channel, ProtocolInjector.this, playerFactory).inject();
@@ -180,6 +186,12 @@ public class ProtocolInjector implements ChannelListener {
channel.pipeline().addFirst(beginInitProtocol);
ctx.fireChannelRead(msg);
}
@Override
public boolean isSharable() {
// Needed if multiple objects are stored in the bootstrap list
return true;
}
};
FuzzyReflection fuzzy = FuzzyReflection.fromObject(serverConnection, true);
@@ -1,125 +0,0 @@
package com.comphenix.protocol.injector.netty;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.injector.packet.MapContainer;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* Represents a way of accessing the new netty Protocol enum.
* @author Kristian
*/
public abstract class ProtocolRegistry {
/**
* Represents a register we are currently building.
* @author Kristian
*/
protected static class Register {
// The main lookup table
public BiMap<PacketType, Class<?>> typeToClass = HashBiMap.create();
public volatile Set<PacketType> serverPackets = Sets.newHashSet();
public volatile Set<PacketType> clientPackets = Sets.newHashSet();
public List<MapContainer> containers = Lists.newArrayList();
public Register() {
}
/**
* Determine if the current register is outdated.
* @return TRUE if it is, FALSE otherwise.
*/
public boolean isOutdated() {
for (MapContainer container : containers) {
if (container.hasChanged()) {
return true;
}
}
return false;
}
}
protected Class<?> enumProtocol;
// Current register
protected volatile Register register;
public ProtocolRegistry() {
enumProtocol = MinecraftReflection.getEnumProtocolClass();
initialize();
}
/**
* Retrieve an immutable view of the packet type lookup.
* @return The packet type lookup.
*/
public Map<PacketType, Class<?>> getPacketTypeLookup() {
return Collections.unmodifiableMap(register.typeToClass);
}
/**
* Retrieve an immutable view of the class to packet type lookup.
* @return The packet type lookup.
*/
public Map<Class<?>, PacketType> getPacketClassLookup() {
return Collections.unmodifiableMap(register.typeToClass.inverse());
}
/**
* Retrieve every known client packet, from every protocol.
* @return Every client packet.
*/
public Set<PacketType> getClientPackets() {
return Collections.unmodifiableSet(register.clientPackets);
}
/**
* Retrieve every known server packet, from every protocol.
* @return Every server packet.
*/
public Set<PacketType> getServerPackets() {
return Collections.unmodifiableSet(register.serverPackets);
}
/**
* Ensure that our local register is up-to-date with Minecraft.
* <p>
* This operation may block the calling thread.
*/
public synchronized void synchronize() {
// Check if the packet registry has changed
if (register.isOutdated()) {
initialize();
}
}
/**
* Load the packet lookup tables in each protocol.
*/
protected abstract void initialize();
protected abstract void associatePackets(Register register, Map<Integer, Class<?>> lookup, Protocol protocol, Sender sender);
/**
* Retrieve the number of mapping in all the maps.
* @param maps - iterable of maps.
* @return The sum of all the entries.
*/
protected final int sum(Iterable<? extends Map<Integer, Class<?>>> maps) {
int count = 0;
for (Map<Integer, Class<?>> map : maps)
count += map.size();
return count;
}
}
@@ -43,8 +43,11 @@ public class SocketAdapter extends Socket {
this.ch = ch;
}
public static SocketAdapter adapt(SocketChannel ch) {
return new SocketAdapter(ch);
public static SocketAdapter adapt(Channel ch) {
if (!(ch instanceof SocketChannel)) {
return null;
}
return new SocketAdapter((SocketChannel) ch);
}
@Override
@@ -17,42 +17,275 @@
package com.comphenix.protocol.injector.packet;
import java.util.Map;
import java.util.Set;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.injector.netty.NettyProtocolRegistry;
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Function;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* Static packet registry in Minecraft.
* @author Kristian
*/
@SuppressWarnings("rawtypes")
public class PacketRegistry {
// The Netty packet registry
private static volatile ProtocolRegistry NETTY;
// Whether or not the registry has been initialized
private static volatile boolean INITIALIZED = false;
/**
* Represents a register we are currently building.
* @author Kristian
*/
protected static class Register {
// The main lookup table
private final Map<PacketType, Optional<Class<?>>> typeToClass = new ConcurrentHashMap<>();
private final Map<Class<?>, PacketType> classToType = new ConcurrentHashMap<>();
private volatile Set<PacketType> serverPackets = Sets.newHashSet();
private volatile Set<PacketType> clientPackets = Sets.newHashSet();
private final List<MapContainer> containers = Lists.newArrayList();
public Register() {}
public void registerPacket(PacketType type, Class<?> clazz, Sender sender) {
typeToClass.put(type, Optional.of(clazz));
classToType.put(clazz, type);
if (sender == Sender.CLIENT) {
clientPackets.add(type);
} else {
serverPackets.add(type);
}
}
public void addContainer(MapContainer container) {
containers.add(container);
}
/**
* Determine if the current register is outdated.
* @return TRUE if it is, FALSE otherwise.
*/
public boolean isOutdated() {
for (MapContainer container : containers) {
if (container.hasChanged()) {
return true;
}
}
return false;
}
}
protected static final Class<?> ENUM_PROTOCOL = MinecraftReflection.getEnumProtocolClass();
// Current register
protected static volatile Register REGISTER;
/**
* Ensure that our local register is up-to-date with Minecraft.
* <p>
* This operation may block the calling thread.
*/
public static synchronized void synchronize() {
// Check if the packet registry has changed
if (REGISTER.isOutdated()) {
initialize();
}
}
protected static synchronized Register createOldRegister() {
Object[] protocols = ENUM_PROTOCOL.getEnumConstants();
// ID to Packet class maps
Map<Object, Map<Integer, Class<?>>> serverMaps = Maps.newLinkedHashMap();
Map<Object, Map<Integer, Class<?>>> clientMaps = Maps.newLinkedHashMap();
Register result = new Register();
StructureModifier<Object> modifier = null;
// Iterate through the protocols
for (Object protocol : protocols) {
if (modifier == null) {
modifier = new StructureModifier<>(protocol.getClass().getSuperclass(), false);
}
StructureModifier<Map<Object, Map<Integer, Class<?>>>> maps = modifier.withTarget(protocol).withType(Map.class);
for (Map.Entry<Object, Map<Integer, Class<?>>> entry : maps.read(0).entrySet()) {
String direction = entry.getKey().toString();
if (direction.contains("CLIENTBOUND")) { // Sent by Server
serverMaps.put(protocol, entry.getValue());
} else if (direction.contains("SERVERBOUND")) { // Sent by Client
clientMaps.put(protocol, entry.getValue());
}
}
}
// Maps we have to occasionally check have changed
for (Object map : serverMaps.values()) {
result.addContainer(new MapContainer(map));
}
for (Object map : clientMaps.values()) {
result.addContainer(new MapContainer(map));
}
for (Object protocol : protocols) {
Enum<?> enumProtocol = (Enum<?>) protocol;
PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol);
// Associate known types
if (serverMaps.containsKey(protocol))
associatePackets(result, serverMaps.get(protocol), equivalent, Sender.SERVER);
if (clientMaps.containsKey(protocol))
associatePackets(result, clientMaps.get(protocol), equivalent, Sender.CLIENT);
}
return result;
}
@SuppressWarnings("unchecked")
private static synchronized Register createNewRegister() {
Object[] protocols = ENUM_PROTOCOL.getEnumConstants();
// ID to Packet class maps
Map<Object, Map<Class<?>, Integer>> serverMaps = Maps.newLinkedHashMap();
Map<Object, Map<Class<?>, Integer>> clientMaps = Maps.newLinkedHashMap();
Register result = new Register();
Field mainMapField = null;
Field packetMapField = null;
// Iterate through the protocols
for (Object protocol : protocols) {
if (mainMapField == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(protocol.getClass(), true);
mainMapField = fuzzy.getField(FuzzyFieldContract.newBuilder()
.banModifier(Modifier.STATIC)
.requireModifier(Modifier.FINAL)
.typeDerivedOf(Map.class)
.build());
mainMapField.setAccessible(true);
}
Map<Object, Object> directionMap;
try {
directionMap = (Map<Object, Object>) mainMapField.get(protocol);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to access packet map", ex);
}
for (Map.Entry<Object, Object> entry : directionMap.entrySet()) {
Object holder = entry.getValue();
if (packetMapField == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(holder.getClass(), true);
packetMapField = fuzzy.getField(FuzzyFieldContract.newBuilder()
.banModifier(Modifier.STATIC)
.requireModifier(Modifier.FINAL)
.typeDerivedOf(Map.class)
.build());
packetMapField.setAccessible(true);
}
Map<Class<?>, Integer> packetMap;
try {
packetMap = (Map<Class<?>, Integer>) packetMapField.get(holder);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to access packet map", ex);
}
String direction = entry.getKey().toString();
if (direction.contains("CLIENTBOUND")) { // Sent by Server
serverMaps.put(protocol, packetMap);
} else if (direction.contains("SERVERBOUND")) { // Sent by Client
clientMaps.put(protocol, packetMap);
}
}
}
// Maps we have to occasionally check have changed
// TODO: Find equivalent in Object2IntMap
/* for (Object map : serverMaps.values()) {
result.containers.add(new MapContainer(map));
}
for (Object map : clientMaps.values()) {
result.containers.add(new MapContainer(map));
} */
for (Object protocol : protocols) {
Enum<?> enumProtocol = (Enum<?>) protocol;
PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol);
// Associate known types
if (serverMaps.containsKey(protocol)) {
associatePackets(result, reverse(serverMaps.get(protocol)), equivalent, Sender.SERVER);
}
if (clientMaps.containsKey(protocol)) {
associatePackets(result, reverse(clientMaps.get(protocol)), equivalent, Sender.CLIENT);
}
}
return result;
}
/**
* Reverses a key->value map to value->key
* Non-deterministic behavior when multiple keys are mapped to the same value
*/
private static <K, V> Map<V, K> reverse(Map<K, V> map) {
Map<V, K> newMap = new HashMap<>(map.size());
for (Map.Entry<K, V> entry : map.entrySet()) {
newMap.put(entry.getValue(), entry.getKey());
}
return newMap;
}
protected static void associatePackets(Register register, Map<Integer, Class<?>> lookup, PacketType.Protocol protocol, Sender sender) {
for (Map.Entry<Integer, Class<?>> entry : lookup.entrySet()) {
PacketType type = PacketType.fromCurrent(protocol, sender, entry.getKey(), entry.getValue());
try {
register.registerPacket(type, entry.getValue(), sender);
} catch (Exception ex) {
ProtocolLogger.debug("Encountered an exception associating packet " + type, ex);
}
}
}
private static void associate(PacketType type, Class<?> clazz) {
if (clazz != null) {
REGISTER.typeToClass.put(type, Optional.of(clazz));
REGISTER.classToType.put(clazz, type);
} else {
REGISTER.typeToClass.put(type, Optional.empty());
}
}
/**
* Initializes the packet registry.
*/
private static void initialize() {
if (INITIALIZED) {
if (NETTY == null) {
throw new IllegalStateException("Failed to initialize packet registry.");
}
return;
}
if (!INITIALIZED) {
INITIALIZED = true;
NETTY = new NettyProtocolRegistry();
INITIALIZED = true;
if (MinecraftVersion.BEE_UPDATE.atOrAbove()) {
REGISTER = createNewRegister();
} else {
REGISTER = createOldRegister();
}
}
}
/**
@@ -62,35 +295,7 @@ public class PacketRegistry {
*/
public static boolean isSupported(PacketType type) {
initialize();
return NETTY.getPacketTypeLookup().containsKey(type);
}
/**
* Retrieve a map of every packet class to every ID.
* <p>
* Deprecated: Use {@link #getPacketToType()} instead.
* @return A map of packet classes and their corresponding ID.
*/
@Deprecated
public static Map<Class, Integer> getPacketToID() {
initialize();
@SuppressWarnings("unchecked")
Map<Class, Integer> result = (Map) Maps.transformValues(
NETTY.getPacketClassLookup(), PacketType::getCurrentId);
return result;
}
/**
* Retrieve a map of every packet class to the respective packet type.
* @return A map of packet classes and their corresponding packet type.
*/
public static Map<Class, PacketType> getPacketToType() {
initialize();
@SuppressWarnings("unchecked")
Map<Class, PacketType> result = (Map) NETTY.getPacketClassLookup();
return result;
return tryGetPacketClass(type).isPresent();
}
/**
@@ -99,9 +304,9 @@ public class PacketRegistry {
*/
public static Set<PacketType> getServerPacketTypes() {
initialize();
NETTY.synchronize();
synchronize();
return NETTY.getServerPackets();
return Collections.unmodifiableSet(REGISTER.serverPackets);
}
/**
@@ -110,88 +315,68 @@ public class PacketRegistry {
*/
public static Set<PacketType> getClientPacketTypes() {
initialize();
NETTY.synchronize();
synchronize();
return NETTY.getClientPackets();
return Collections.unmodifiableSet(REGISTER.clientPackets);
}
private static Class<?> searchForPacket(List<String> classNames) {
for (String name : classNames) {
try {
Class<?> clazz = MinecraftReflection.getMinecraftClass(name);
if (MinecraftReflection.getPacketClass().isAssignableFrom(clazz)
&& !Modifier.isAbstract(clazz.getModifiers())) {
return clazz;
}
} catch (Exception ignored) {}
}
return null;
}
/**
* Retrieves the correct packet class from a given packet ID.
* <p>
* Deprecated: Use {@link #getPacketClassFromType(PacketType)} instead.
* @param packetID - the packet ID.
* @return The associated class.
*/
@Deprecated
public static Class getPacketClassFromID(int packetID) {
initialize();
return NETTY.getPacketTypeLookup().get(PacketType.findLegacy(packetID));
}
/**
* Retrieves the correct packet class from a given type.
* @param type - the packet type.
* @return The associated class.
*/
public static Class getPacketClassFromType(PacketType type) {
return getPacketClassFromType(type, false);
}
/**
* Retrieves the correct packet class from a given type.
* <p>
* Note that forceVanillla will be ignored on MC 1.7.2 and later.
*
* @param type - the packet type.
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
* @return The associated class.
* @deprecated forceVanilla no longer has any effect
*/
public static Class getPacketClassFromType(PacketType type, boolean forceVanilla) {
@Deprecated
public static Class<?> getPacketClassFromType(PacketType type, boolean forceVanilla) {
return getPacketClassFromType(type);
}
public static Optional<Class<?>> tryGetPacketClass(PacketType type) {
initialize();
// Try the lookup first
Class<?> clazz = NETTY.getPacketTypeLookup().get(type);
if (clazz != null) {
return clazz;
// Try the lookup first (may be null, so check contains)
Optional<Class<?>> res = REGISTER.typeToClass.get(type);
if (res != null) {
return res;
}
// Then try looking up the class names
for (String name : type.getClassNames()) {
try {
clazz = MinecraftReflection.getMinecraftClass(name);
break;
} catch (Exception ignored) { }
Class<?> clazz = searchForPacket(type.getClassNames());
if (clazz != null) {
// we'd like for it to be associated correctly from the get-go; this is OK on older versions though
ProtocolLogger.warnAbove(type.getCurrentVersion(), "Updating associated class for {0} to {1}", type.name(), clazz);
}
// TODO Cache the result?
return clazz;
// cache it for next time
associate(type, clazz);
return Optional.ofNullable(clazz);
}
/**
* Retrieves the correct packet class from a given packet ID.
* <p>
* This method has been deprecated.
* @param packetID - the packet ID.
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
* @return The associated class.
* Get the packet class associated with a given type. First attempts to read from the
* type-to-class mapping, and tries
* @param type the packet type
* @return The associated class
*/
@Deprecated
public static Class getPacketClassFromID(int packetID, boolean forceVanilla) {
initialize();
return getPacketClassFromID(packetID);
}
/**
* Retrieve the packet ID of a given packet.
* <p>
* Deprecated: Use {@link #getPacketType(Class)}.
* @param packet - the type of packet to check.
* @return The legacy ID of the given packet.
* @throws IllegalArgumentException If this is not a valid packet.
*/
@Deprecated
public static int getPacketID(Class<?> packet) {
initialize();
return NETTY.getPacketClassLookup().get(packet).getCurrentId();
public static Class<?> getPacketClassFromType(PacketType type) {
return tryGetPacketClass(type)
.orElseThrow(() -> new IllegalArgumentException("Could not find packet for type " + type.name()));
}
/**
@@ -200,7 +385,8 @@ public class PacketRegistry {
* @return The packet type, or NULL if not found.
*/
public static PacketType getPacketType(Class<?> packet) {
return getPacketType(packet, null);
initialize();
return REGISTER.classToType.get(packet);
}
/**
@@ -208,9 +394,10 @@ public class PacketRegistry {
* @param packet - the class of the packet.
* @param sender - the sender of the packet, or NULL.
* @return The packet type, or NULL if not found.
* @deprecated sender no longer has any effect
*/
@Deprecated
public static PacketType getPacketType(Class<?> packet, Sender sender) {
initialize();
return NETTY.getPacketClassLookup().get(packet);
return getPacketType(packet);
}
}
@@ -80,4 +80,9 @@ public class BukkitSocketInjector implements SocketInjector {
public void setUpdatedPlayer(Player updatedPlayer) {
this.player = updatedPlayer;
}
@Override
public boolean isConnected() {
return player.isOnline();
}
}
@@ -18,22 +18,24 @@ public interface SocketInjector {
* Retrieve the associated socket of this player.
* @return The associated socket.
* @throws IllegalAccessException If we're unable to read the socket field.
* @deprecated May be null on certain server implementations. Also don't use raw sockets.
*/
public abstract Socket getSocket() throws IllegalAccessException;
@Deprecated
Socket getSocket() throws IllegalAccessException;
/**
* Retrieve the associated address of this player.
* @return The associated address.
* @throws IllegalAccessException If we're unable to read the socket field.
*/
public abstract SocketAddress getAddress() throws IllegalAccessException;
SocketAddress getAddress() throws IllegalAccessException;
/**
* Attempt to disconnect the current client.
* @param message - the message to display.
* @throws InvocationTargetException If disconnection failed.
*/
public abstract void disconnect(String message) throws InvocationTargetException;
void disconnect(String message) throws InvocationTargetException;
/**
* Send a packet to the client.
@@ -42,30 +44,36 @@ public interface SocketInjector {
* @param filtered - whether or not the packet will be filtered by our listeners.
* @throws InvocationTargetException If an error occured when sending the packet.
*/
public abstract void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered)
void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered)
throws InvocationTargetException;
/**
* Retrieve the hooked player.
* @return The hooked player.
*/
public abstract Player getPlayer();
Player getPlayer();
/**
* Retrieve the hooked player object OR the more up-to-date player instance.
* @return The hooked player, or a more up-to-date instance.
*/
public abstract Player getUpdatedPlayer();
Player getUpdatedPlayer();
/**
* Invoked when a delegated socket injector transfers the state of one injector to the next.
* @param delegate - the new injector.
*/
public abstract void transferState(SocketInjector delegate);
void transferState(SocketInjector delegate);
/**
* Set the real Bukkit player that we will use.
* @param updatedPlayer - the real Bukkit player.
*/
public abstract void setUpdatedPlayer(Player updatedPlayer);
void setUpdatedPlayer(Player updatedPlayer);
/**
* Determines if the player is currently connected.
* @return true if the player is connected.
*/
boolean isConnected();
}
@@ -157,9 +157,9 @@ public class TemporaryPlayerFactory {
// Methods that are supported in the fallback instance
if (methodName.equals("isOnline"))
return injector.getSocket() != null && injector.getSocket().isConnected();
return injector.isConnected();
else if (methodName.equals("getName"))
return "UNKNOWN[" + injector.getSocket().getRemoteSocketAddress() + "]";
return "UNKNOWN[" + injector.getAddress() + "]";
// Ignore all other methods
throw new UnsupportedOperationException(
@@ -2,7 +2,6 @@ package com.comphenix.protocol.metrics;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.craftbukkit.libs.org.apache.commons.lang3.tuple.Pair;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
@@ -24,7 +24,6 @@ import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.utility.Util;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.libs.org.apache.commons.lang3.tuple.Pair;
import java.io.IOException;
import java.util.HashMap;
@@ -87,13 +86,7 @@ public class Statistics {
for (PacketListener listener : ProtocolLibrary.getProtocolManager().getPacketListeners()) {
String name = PacketAdapter.getPluginName(listener);
// Increment occurence
if (!users.containsKey(name)) {
users.put(name, 1);
} else {
users.put(name, users.get(name) + 1);
}
users.put(name, 1);
}
return users;
@@ -17,6 +17,7 @@ package com.comphenix.protocol.reflect;
* limitations under the License.
*/
import com.comphenix.protocol.reflect.accessors.Accessors;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
@@ -394,20 +395,12 @@ public class FieldUtils {
writeStaticField(field, value);
}
/**
* @deprecated Use {@link #writeStaticField(Class, String, Object, boolean)} instead.
*/
@Deprecated
public static void writeStaticFinalField(Class<?> clazz, String fieldName, Object value, boolean forceAccess) throws Exception {
Field field = getField(clazz, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate field " + fieldName + " in " + clazz);
}
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.setAccessible(true);
field.set(null, value);
writeStaticField(clazz, fieldName, value, forceAccess);
}
/**
@@ -442,12 +435,8 @@ public class FieldUtils {
if (field == null) {
throw new IllegalArgumentException("The field must not be null");
}
if (forceAccess && !field.isAccessible()) {
field.setAccessible(true);
} else {
MemberUtils.setAccessibleWorkaround(field);
}
field.set(target, value);
Accessors.getFieldAccessor(field, forceAccess).set(target, value);
}
/**
@@ -117,12 +117,11 @@ public class ObjectWriter {
}
// Copy private fields underneath
Class<?> superclass = commonType.getSuperclass();
if (superclass != null && !superclass.equals(Object.class)) {
copyToInternal(source, destination, superclass, false);
}
// Class<?> superclass = commonType.getSuperclass();
//
// if (superclass != null && !superclass.equals(Object.class)) {
// copyToInternal(source, destination, superclass, false);
// }
} catch (FieldAccessException e) {
throw new RuntimeException("Unable to copy fields from " + commonType.getName(), e);
}
@@ -308,7 +308,9 @@ public class StructureModifier<TField> {
* @param fieldIndex - index of the field.
* @param value - TRUE if this field should be read only, FALSE otherwise.
* @throws FieldAccessException If we cannot modify the read-only status.
* @deprecated In recent java versions (starting at 9) the modifier field is secured and will not be writeable.
*/
@Deprecated
public void setReadOnly(int fieldIndex, boolean value) throws FieldAccessException {
if (fieldIndex < 0 || fieldIndex >= data.size())
throw new IllegalArgumentException("Index parameter is not within [0 - " + data.size() + ")");
@@ -325,7 +327,9 @@ public class StructureModifier<TField> {
* @param field - the field to change.
* @param isReadOnly - TRUE if the field should be read only, FALSE otherwise.
* @throws IllegalAccessException If an error occured.
* @deprecated In recent java versions (starting at 9) the modifier field is secured and will not be writeable.
*/
@Deprecated
protected static void setFinalState(Field field, boolean isReadOnly) throws IllegalAccessException {
if (isReadOnly)
FieldUtils.writeField((Object) field, "modifiers", field.getModifiers() | Modifier.FINAL, true);
@@ -289,6 +289,7 @@ public final class Accessors {
* @return The method accessor.
*/
public static ConstructorAccessor getConstructorAccessor(final Constructor<?> constructor) {
constructor.setAccessible(true); // let us in even if we are not allowed to
return new DefaultConstrutorAccessor(constructor);
}
@@ -1,14 +1,28 @@
package com.comphenix.protocol.reflect.accessors;
import com.comphenix.protocol.ProtocolLogger;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.WrongMethodTypeException;
import java.lang.reflect.Field;
final class DefaultFieldAccessor implements FieldAccessor {
private final Field field;
private MethodHandle setter;
public DefaultFieldAccessor(Field field) {
this.field = field;
// try to get a getter and setter handle for the field
if (UnsafeFieldAccess.hasTrustedLookup()) {
try {
setter = UnsafeFieldAccess.findSetter(field);
} catch (ReflectiveOperationException exception) {
ProtocolLogger.debug("Unable to get setter for field " + field, exception);
}
}
}
@Override
public Object get(Object instance) {
try {
@@ -19,18 +33,25 @@ final class DefaultFieldAccessor implements FieldAccessor {
throw new IllegalStateException("Cannot use reflection.", e);
}
}
@Override
public void set(Object instance, Object value) {
try {
field.set(instance, value);
} catch (IllegalArgumentException e) {
if (setter == null) {
field.set(instance, value);
} else {
setter.invoke(instance, value);
}
} catch (IllegalArgumentException | ClassCastException e) {
throw new RuntimeException("Cannot set field " + field + " to value " + value, e);
} catch (IllegalAccessException e) {
} catch (IllegalAccessException | WrongMethodTypeException e) {
throw new IllegalStateException("Cannot use reflection.", e);
} catch (Throwable ignored) {
// cannot happen - this might only occur when the handle targets a method
throw new RuntimeException("Cannot happen");
}
}
@Override
public Field getField() {
return field;
@@ -43,16 +64,17 @@ final class DefaultFieldAccessor implements FieldAccessor {
@Override
public boolean equals(Object obj) {
if (this == obj)
if (this == obj) {
return true;
}
if (obj instanceof DefaultFieldAccessor) {
DefaultFieldAccessor other = (DefaultFieldAccessor) obj;
return other.field == field;
}
return true;
}
@Override
public String toString() {
return "DefaultFieldAccessor [field=" + field + "]";
@@ -0,0 +1,48 @@
package com.comphenix.protocol.reflect.accessors;
import com.comphenix.protocol.ProtocolLogger;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.logging.Level;
final class UnsafeFieldAccess {
private static final Lookup TRUSTED_LOOKUP;
static {
Lookup trusted = null;
try {
// get the unsafe class
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
// get the unsafe instance
Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
sun.misc.Unsafe unsafe = (sun.misc.Unsafe) theUnsafe.get(null);
// get the trusted lookup field
Field trustedLookup = Lookup.class.getDeclaredField("IMPL_LOOKUP");
// get access to the base and offset value of it
long offset = unsafe.staticFieldOffset(trustedLookup);
Object baseValue = unsafe.staticFieldBase(trustedLookup);
// get the trusted lookup instance
trusted = (Lookup) unsafe.getObject(baseValue, offset);
} catch (Exception exception) {
ProtocolLogger.log(Level.SEVERE, "Unable to retrieve trusted lookup", exception);
}
TRUSTED_LOOKUP = trusted;
}
public static boolean hasTrustedLookup() {
return TRUSTED_LOOKUP != null;
}
public static MethodHandle findSetter(Field field) throws ReflectiveOperationException {
if (Modifier.isStatic(field.getModifiers())) {
return TRUSTED_LOOKUP.findStaticSetter(field.getDeclaringClass(), field.getName(), field.getType());
} else {
return TRUSTED_LOOKUP.findSetter(field.getDeclaringClass(), field.getName(), field.getType());
}
}
}
@@ -256,11 +256,11 @@ public class AggregateCloner implements Cloner {
if (index < cloners.size()) {
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);
}
// try {
return cloner.clone(source);
// } catch (Exception ex) {
// throw new RuntimeException("Failed to clone " + source + " (" + source.getClass() + ") with " + cloner, ex);
// }
}
// Damn - failure
@@ -88,6 +88,11 @@ public class BukkitCloner implements Cloner {
fromManual(ComponentConverter::getBaseComponentArrayClass, source ->
ComponentConverter.clone((BaseComponent[]) source));
} catch (Throwable ignored) { }
try {
fromManual(AdventureComponentConverter::getComponentClass, source ->
AdventureComponentConverter.clone(source));
} catch (Throwable ignored) { }
}
private Function<Object, Object> findCloner(Class<?> type) {
@@ -62,29 +62,29 @@ public class ImmutableDetector implements Cloner {
add(MinecraftReflection::getDataWatcherSerializerClass);
add(MinecraftReflection::getBlockClass);
add(MinecraftReflection::getItemClass);
add("SoundEffect");
add("sounds.SoundEffect", "SoundEffect");
if (MinecraftVersion.AQUATIC_UPDATE.atOrAbove()) {
add(MinecraftReflection::getFluidTypeClass);
add(MinecraftReflection::getParticleTypeClass);
add("Particle");
add("core.particles.Particle", "Particle");
}
if (MinecraftVersion.VILLAGE_UPDATE.atOrAbove()) {
add("EntityTypes");
add("VillagerType");
add("VillagerProfession");
add(MinecraftReflection::getEntityTypes);
add("world.entity.npc.VillagerType", "VillagerType");
add("world.entity.npc.VillagerProfession", "VillagerProfession");
}
// TODO automatically detect the technically-not-an-enum enums that Mojang is so fond of
// Would also probably go in tandem with having the FieldCloner use this
if (MinecraftVersion.NETHER_UPDATE.atOrAbove()) {
add("IRegistry");
add("core.IRegistry", "IRegistry");
}
if (MinecraftVersion.NETHER_UPDATE_2.atOrAbove()) {
add("ResourceKey");
add(MinecraftReflection::getResourceKey);
}
}
@@ -97,13 +97,11 @@ public class ImmutableDetector implements Cloner {
} catch (RuntimeException ignored) { }
}
private static void add(String className) {
try {
Class<?> clazz = MinecraftReflection.getMinecraftClass(className);
if (clazz != null) {
immutableNMS.add(clazz);
}
} catch (RuntimeException ignored) { }
private static void add(String className, String... aliases) {
Class<?> clazz = MinecraftReflection.getNullableNMS(className, aliases);
if (clazz != null) {
immutableNMS.add(clazz);
}
}
@Override
@@ -58,8 +58,6 @@ public abstract class CompiledStructureModifier extends StructureModifier<Object
throw new IllegalStateException("Cannot make compiled field " + fieldIndex + " read only.");
}
}
super.setReadOnly(fieldIndex, value);
}
// Speed up the default writer
@@ -141,6 +141,18 @@ public final class StructureCompiler {
private static String COMPILED_CLASS = PACKAGE_NAME + "/CompiledStructureModifier";
private static String FIELD_EXCEPTION_CLASS = "com/comphenix/protocol/reflect/FieldAccessException";
// On java 9+ (53.0+) CLassLoader#defineClass(String, byte[], int, int) should not be used anymore.
// It will throw warnings and on Java 16+ (60.0+), it does not work at all anymore.
private static final boolean LEGACY_CLASS_DEFINITION =
Float.parseFloat(System.getProperty("java.class.version")) < 53;
/**
* The MethodHandles.Lookup object for this compiler. Only used when using the modern defineClass strategy.
*/
private Object lookup = null;
// Used to get the MethodHandles.Lookup object on newer versions of Java.
private volatile static Method lookupMethod;
public static boolean attemptClassLoad = false;
/**
@@ -289,36 +301,54 @@ public final class StructureCompiler {
byte[] data = cw.toByteArray();
// Call the define method
Class<?> clazz = defineClass(data);
// 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;
}
private Class<?> defineClassLegacy(byte[] data) throws InvocationTargetException, IllegalAccessException,
NoSuchMethodException {
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;
}
return (Class<?>) defineMethod.invoke(loader, null, data, 0, data.length);
}
private Class<?> defineClassModern(byte[] data) throws InvocationTargetException, IllegalAccessException,
ClassNotFoundException, NoSuchMethodException {
if (defineMethod == null) {
defineMethod = Class.forName("java.lang.invoke.MethodHandles$Lookup")
.getDeclaredMethod("defineClass", byte[].class);
}
if (lookupMethod == null) {
lookupMethod = Class.forName("java.lang.invoke.MethodHandles").getDeclaredMethod("lookup");
}
if (lookup == null)
lookup = lookupMethod.invoke(null);
return (Class<?>) defineMethod.invoke(lookup, data);
}
private Class<?> defineClass(byte[] data) {
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;
return LEGACY_CLASS_DEFINITION ? defineClassLegacy(data) : defineClassModern(data);
} catch (SecurityException e) {
throw new RuntimeException("Cannot use reflection to dynamically load a class.", e);
} catch (NoSuchMethodException e) {
} catch (NoSuchMethodException | ClassNotFoundException e) {
throw new IllegalStateException("Incompatible JVM.", e);
} catch (IllegalArgumentException e) {
throw new IllegalStateException("Cannot call defineMethod - wrong JVM?", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Security limitation! Cannot dynamically load class.", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Error occured in code generator.", e);
throw new RuntimeException("Error occurred in code generator.", e);
}
}
@@ -17,32 +17,31 @@
package com.comphenix.protocol.reflect.instances;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import javax.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import javax.annotation.Nullable;
import com.comphenix.protocol.ProtocolLogger;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
/**
* Used to construct default instances of any type.
* @author Kristian
*
*/
public class DefaultInstances implements InstanceProvider {
public static final InstanceProvider UUID_GENERATOR = type -> type == UUID.class ? new UUID(0L, 0L) : null;
/**
* Standard default instance provider.
*/
public static final DefaultInstances DEFAULT = DefaultInstances.fromArray(
PrimitiveGenerator.INSTANCE, CollectionGenerator.INSTANCE, UUID_GENERATOR);
PrimitiveGenerator.INSTANCE,
CollectionGenerator.INSTANCE,
MinecraftGenerator.INSTANCE
);
/**
* The maximum height of the heirarchy of creates types. Used to prevent cycles.
@@ -188,7 +187,7 @@ public class DefaultInstances implements InstanceProvider {
// Note that we don't allow recursive types - that is, types that
// require itself in the constructor.
if (types.length < lastCount) {
if (!contains(types, type)) {
if (!contains(types, type) && !contains(types, MinecraftReflection.getPacketDataSerializerClass())) {
if (nonNull) {
// Make sure all of these types are non-null
if (isAnyNull(types, providers, recursionLevel)) {
@@ -273,7 +272,6 @@ public class DefaultInstances implements InstanceProvider {
Constructor<T> minimum = getMinimumConstructor(type, providers, recursionLevel + 1);
// Create the type with this constructor using default values. This might fail, though.
// TODO every packet has a zero-args constructor
try {
if (minimum != null) {
@@ -0,0 +1,77 @@
package com.comphenix.protocol.reflect.instances;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BukkitConverters;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class MinecraftGenerator {
// system unique id representation
public static final UUID SYS_UUID;
// minecraft default types
public static final Object AIR_ITEM_STACK;
private static Object DEFAULT_ENTITY_TYPES; // modern servers only (older servers will use an entity type id)
// minecraft method accessors
private static final MethodAccessor NON_NULL_LIST_CREATE;
// fast util mappings for paper relocation
private static final Map<Class<?>, Constructor<?>> FAST_MAP_CONSTRUCTORS;
static {
try {
SYS_UUID = new UUID(0L, 0L);
AIR_ITEM_STACK = BukkitConverters.getItemStackConverter().getGeneric(new ItemStack(Material.AIR));
FAST_MAP_CONSTRUCTORS = new ConcurrentHashMap<>();
NON_NULL_LIST_CREATE = MinecraftReflection.getNonNullListCreateAccessor();
} catch (Throwable ex) {
throw new RuntimeException("Failed to create static fields in MinecraftGenerator", ex);
}
}
public static final InstanceProvider INSTANCE = type -> {
if (type != null) {
if (type == UUID.class) {
return SYS_UUID;
} else if (type.isEnum()) {
return type.getEnumConstants()[0];
} else if (type == MinecraftReflection.getItemStackClass()) {
return AIR_ITEM_STACK;
} else if (type == MinecraftReflection.getEntityTypes()) {
if (DEFAULT_ENTITY_TYPES == null) {
// try to initialize now
try {
DEFAULT_ENTITY_TYPES = BukkitConverters.getEntityTypeConverter().getGeneric(EntityType.AREA_EFFECT_CLOUD);
} catch (Exception ignored) {
// not available in this version of minecraft
}
}
return DEFAULT_ENTITY_TYPES;
} else if (type.isAssignableFrom(Map.class)) {
Constructor<?> ctor = FAST_MAP_CONSTRUCTORS.computeIfAbsent(type, __ -> {
try {
String name = type.getCanonicalName();
if (name != null && name.contains("it.unimi.fastutils")) {
return Class.forName(name.substring(name.length() - 3) + "OpenHashMap").getConstructor();
}
} catch (Exception ignored) {}
return null;
});
if (ctor != null) {
try {
return ctor.newInstance();
} catch (ReflectiveOperationException ignored) {}
}
} else if (NON_NULL_LIST_CREATE != null && type == MinecraftReflection.getNonNullListClass()) {
return NON_NULL_LIST_CREATE.invoke(null);
}
}
return null;
};
}
@@ -21,10 +21,10 @@ package com.comphenix.protocol.utility;
*/
public final class Constants {
public static final String PACKAGE_VERSION = "v1_16_R3";
public static final String NMS = "net.minecraft.server." + PACKAGE_VERSION;
public static final String PACKAGE_VERSION = "v1_18_R2";
public static final String NMS = "net.minecraft";
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
public static final MinecraftVersion CURRENT_VERSION = MinecraftVersion.NETHER_UPDATE_2;
public static final MinecraftVersion CURRENT_VERSION = MinecraftVersion.CAVES_CLIFFS_2;
public static void init() {
MinecraftReflection.setMinecraftPackage(NMS, OBC);
@@ -15,15 +15,16 @@ public class MinecraftFields {
// Cached accessors
private static volatile FieldAccessor CONNECTION_ACCESSOR;
private static volatile FieldAccessor NETWORK_ACCESSOR;
private static volatile FieldAccessor CONNECTION_ENTITY_ACCESSOR;
private MinecraftFields() {
// Not constructable
}
/**
* Retrieve the network mananger associated with a particular player.
* Retrieve the network manager associated with a particular player.
* @param player - the player.
* @return The network manager, or NULL if no network manager has been asssociated yet.
* @return The network manager, or NULL if no network manager has been associated yet.
*/
public static Object getNetworkManager(Player player) {
Object nmsPlayer = BukkitUnwrapper.getInstance().unwrapItem(player);
@@ -50,9 +51,13 @@ public class MinecraftFields {
Preconditions.checkNotNull(player, "player cannot be null!");
return getPlayerConnection(BukkitUnwrapper.getInstance().unwrapItem(player));
}
// Retrieve player connection from a native instance
private static Object getPlayerConnection(Object nmsPlayer) {
/**
* Retrieve the PlayerConnection (or NetServerHandler) associated with a player.
* @param nmsPlayer - the NMS player.
* @return The player connection.
*/
public static Object getPlayerConnection(Object nmsPlayer) {
Preconditions.checkNotNull(nmsPlayer, "nmsPlayer cannot be null!");
if (CONNECTION_ACCESSOR == null) {
@@ -61,4 +66,21 @@ public class MinecraftFields {
}
return CONNECTION_ACCESSOR.get(nmsPlayer);
}
/**
* Retrieves the EntityPlayer player field from a PlayerConnection.
*
* @param playerConnection The PlayerConnection object from which to retrieve the EntityPlayer field.
* @return The value of the EntityPlayer field in the PlayerConnection.
*/
public static Object getPlayerFromConnection(Object playerConnection) {
Preconditions.checkNotNull(playerConnection, "playerConnection cannot be null!");
if (CONNECTION_ENTITY_ACCESSOR == null) {
Class<?> connectionClass = MinecraftReflection.getPlayerConnectionClass();
Class<?> entityPlayerClass = MinecraftReflection.getEntityPlayerClass();
CONNECTION_ENTITY_ACCESSOR = Accessors.getFieldAccessor(connectionClass, entityPlayerClass, true);
}
return CONNECTION_ENTITY_ACCESSOR.get(playerConnection);
}
}
@@ -248,8 +248,8 @@ public class MinecraftMethods {
}
}
if (packetReadByteBuf == null)
throw new IllegalStateException("Unable to find Packet.read(PacketDataSerializer)");
// if (packetReadByteBuf == null)
// throw new IllegalStateException("Unable to find Packet.read(PacketDataSerializer)");
if (packetWriteByteBuf == null)
throw new IllegalStateException("Unable to find Packet.write(PacketDataSerializer)");
}
@@ -72,6 +72,15 @@ public class MinecraftProtocolVersion {
map.put(new MinecraftVersion(1, 16, 2), 751);
map.put(new MinecraftVersion(1, 16, 3), 753);
map.put(new MinecraftVersion(1, 16, 4), 754);
map.put(new MinecraftVersion(1, 16, 5), 754);
map.put(new MinecraftVersion(1, 17, 0), 755);
map.put(new MinecraftVersion(1, 17, 1), 756);
map.put(new MinecraftVersion(1, 18, 0), 757);
map.put(new MinecraftVersion(1, 18, 1), 757);
map.put(new MinecraftVersion(1, 18, 2), 758);
return map;
}
@@ -27,10 +27,8 @@ import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
@@ -43,6 +41,7 @@ import javax.annotation.Nonnull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.PacketType;
@@ -57,6 +56,7 @@ import com.comphenix.protocol.reflect.ClassAnalyser;
import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
@@ -65,6 +65,7 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavailableException;
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavailableException.Reason;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.comphenix.protocol.wrappers.nbt.NbtType;
import com.google.common.base.Joiner;
@@ -222,30 +223,35 @@ public class MinecraftReflection {
Class<?> craftEntity = getCraftEntityClass();
Method getHandle = craftEntity.getMethod("getHandle");
MINECRAFT_FULL_PACKAGE = getPackage(getHandle.getReturnType().getCanonicalName());
// Pretty important invariantt
if (!MINECRAFT_FULL_PACKAGE.startsWith(MINECRAFT_PREFIX_PACKAGE)) {
// See if we got the Forge entity package
if (MINECRAFT_FULL_PACKAGE.equals(FORGE_ENTITY_PACKAGE)) {
// USe the standard NMS versioned package
MINECRAFT_FULL_PACKAGE = CachedPackage.combine(MINECRAFT_PREFIX_PACKAGE, packageVersion);
} else {
// Assume they're the same instead
MINECRAFT_PREFIX_PACKAGE = MINECRAFT_FULL_PACKAGE;
}
// The package is usualy flat, so go with that assumption
String matcher =
(MINECRAFT_PREFIX_PACKAGE.length() > 0 ?
Pattern.quote(MINECRAFT_PREFIX_PACKAGE + ".") : "") + CANONICAL_REGEX;
// We'll still accept the default location, however
setDynamicPackageMatcher("(" + matcher + ")|(" + MINECRAFT_OBJECT + ")");
} else {
// Use the standard matcher
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
// total rework of the NMS structure in 1.17 (at least there's no versioning)
MINECRAFT_FULL_PACKAGE = MINECRAFT_PREFIX_PACKAGE = "net.minecraft";
setDynamicPackageMatcher(MINECRAFT_OBJECT);
} else {
MINECRAFT_FULL_PACKAGE = getPackage(getHandle.getReturnType().getCanonicalName());
// Pretty important invariantt
if (!MINECRAFT_FULL_PACKAGE.startsWith(MINECRAFT_PREFIX_PACKAGE)) {
// See if we got the Forge entity package
if (MINECRAFT_FULL_PACKAGE.equals(FORGE_ENTITY_PACKAGE)) {
// USe the standard NMS versioned package
MINECRAFT_FULL_PACKAGE = CachedPackage.combine(MINECRAFT_PREFIX_PACKAGE, packageVersion);
} else {
// Assume they're the same instead
MINECRAFT_PREFIX_PACKAGE = MINECRAFT_FULL_PACKAGE;
}
// The package is usualy flat, so go with that assumption
String matcher =
(MINECRAFT_PREFIX_PACKAGE.length() > 0 ? Pattern.quote(MINECRAFT_PREFIX_PACKAGE + ".") : "") + CANONICAL_REGEX;
// We'll still accept the default location, however
setDynamicPackageMatcher("(" + matcher + ")|(" + MINECRAFT_OBJECT + ")");
} else {
// Use the standard matcher
setDynamicPackageMatcher(MINECRAFT_OBJECT);
}
}
return MINECRAFT_FULL_PACKAGE;
@@ -371,6 +377,21 @@ public class MinecraftReflection {
}
}
/**
* Retrieve the Bukkit player from a given PlayerConnection.
* @param playerConnection The PlayerConnection.
* @return A bukkit player.
* @throws RuntimeException If we were unable to retrieve the Bukkit player.
*/
public static Player getBukkitPlayerFromConnection(Object playerConnection)
{
try {
return (Player) getBukkitEntity(MinecraftFields.getPlayerFromConnection(playerConnection));
} catch (Exception e) {
throw new IllegalArgumentException("Cannot get Bukkit entity from connection " + playerConnection, e);
}
}
/**
* Determine if a given object can be found within the package net.minecraft.server.
* @param obj - the object to test.
@@ -580,7 +601,7 @@ public class MinecraftReflection {
*/
public static Class<?> getEntityPlayerClass() {
try {
return getMinecraftClass("EntityPlayer");
return getMinecraftClass("server.level.EntityPlayer","EntityPlayer");
} catch (RuntimeException e) {
try {
// Grab CraftPlayer's handle
@@ -637,7 +658,7 @@ public class MinecraftReflection {
*/
public static Class<?> getEntityClass() {
try {
return getMinecraftClass("Entity");
return getMinecraftClass("server.world.Entity", "Entity");
} catch (RuntimeException e) {
return fallbackMethodReturn("Entity", "entity.CraftEntity", "getHandle");
}
@@ -657,7 +678,7 @@ public class MinecraftReflection {
*/
public static Class<?> getWorldServerClass() {
try {
return getMinecraftClass("WorldServer");
return getMinecraftClass("server.level.WorldServer", "WorldServer");
} catch (RuntimeException e) {
return fallbackMethodReturn("WorldServer", "CraftWorld", "getHandle");
}
@@ -669,7 +690,7 @@ public class MinecraftReflection {
*/
public static Class<?> getNmsWorldClass() {
try {
return getMinecraftClass("World");
return getMinecraftClass("world.level.World", "World");
} catch (RuntimeException e) {
return setMinecraftClass("World", getWorldServerClass().getSuperclass());
}
@@ -696,7 +717,7 @@ public class MinecraftReflection {
*/
public static Class<?> getPacketClass() {
try {
return getMinecraftClass("Packet");
return getMinecraftClass("network.protocol.Packet", "Packet");
} catch (RuntimeException e) {
FuzzyClassContract paketContract = null;
@@ -752,8 +773,10 @@ public class MinecraftReflection {
* @return The Enum protocol class.
*/
public static Class<?> getEnumProtocolClass() {
try {
return getMinecraftClass("EnumProtocol");
return getMinecraftClass("network.EnumProtocol", "EnumProtocol");
/* try {
return getMinecraftClass("network.EnumProtocol", "EnumProtocol");
} catch (RuntimeException e) {
Method protocolMethod = FuzzyReflection.fromClass(getNetworkManagerClass()).getMethod(
FuzzyMethodContract.newBuilder().
@@ -762,7 +785,7 @@ public class MinecraftReflection {
build()
);
return setMinecraftClass("EnumProtocol", protocolMethod.getParameterTypes()[0]);
}
} */
}
/**
@@ -771,7 +794,7 @@ public class MinecraftReflection {
*/
public static Class<?> getIChatBaseComponentClass() {
try {
return getMinecraftClass("IChatBaseComponent");
return getMinecraftClass("network.chat.IChatbaseComponent", "IChatBaseComponent");
} catch (RuntimeException e) {
return setMinecraftClass("IChatBaseComponent",
Accessors.getMethodAccessor(getCraftChatMessage(), "fromString", String.class).
@@ -790,7 +813,7 @@ public class MinecraftReflection {
*/
public static Class<?> getChatComponentTextClass() {
try {
return getMinecraftClass("ChatComponentText");
return getMinecraftClass("network.chat.ChatComponentText", "ChatComponentText");
} catch (RuntimeException e) {
try {
Method getScoreboardDisplayName = FuzzyReflection.fromClass(getEntityClass()).
@@ -818,7 +841,8 @@ public class MinecraftReflection {
*/
public static Class<?> getChatSerializerClass() {
try {
return getMinecraftClass("ChatSerializer", "IChatBaseComponent$ChatSerializer");
return getMinecraftClass("network.chat.IChatBaseComponent$ChatSerializer",
"ChatSerializer", "IChatBaseComponent$ChatSerializer");
} catch (RuntimeException e) {
// TODO: Figure out a functional fallback
throw new IllegalStateException("Could not find ChatSerializer class.", e);
@@ -830,11 +854,8 @@ public class MinecraftReflection {
* @return The ServerPing class.
*/
public static Class<?> getServerPingClass() {
if (!isUsingNetty())
throw new IllegalStateException("ServerPing is only supported in 1.7.2.");
try {
return getMinecraftClass("ServerPing");
return getMinecraftClass("network.protocol.status.ServerPing", "ServerPing");
} catch (RuntimeException e) {
Class<?> statusServerInfo = PacketType.Status.Server.SERVER_INFO.getPacketClass();
@@ -856,11 +877,9 @@ public class MinecraftReflection {
* @return The ServerPingServerData class.
*/
public static Class<?> getServerPingServerDataClass() {
if (!isUsingNetty())
throw new IllegalStateException("ServerPingServerData is only supported in 1.7.2.");
try {
return getMinecraftClass("ServerPingServerData", "ServerPing$ServerData");
return getMinecraftClass("network.protocol.status.ServerPing$ServerData",
"ServerPingServerData", "ServerPing$ServerData");
} catch (RuntimeException e) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(getServerPingClass(), true);
return setMinecraftClass("ServerPingServerData", fuzzy.getFieldByType("(.*)(ServerData)(.*)").getType());
@@ -872,11 +891,9 @@ public class MinecraftReflection {
* @return The ServerPingPlayerSample class.
*/
public static Class<?> getServerPingPlayerSampleClass() {
if (!isUsingNetty())
throw new IllegalStateException("ServerPingPlayerSample is only supported in 1.7.2.");
try {
return getMinecraftClass("ServerPingPlayerSample", "ServerPing$ServerPingPlayerSample");
return getMinecraftClass("network.protocol.status.ServerPing$ServerPingPlayerSample",
"ServerPingPlayerSample", "ServerPing$ServerPingPlayerSample");
} catch (RuntimeException e) {
Class<?> serverPing = getServerPingClass();
@@ -942,7 +959,7 @@ public class MinecraftReflection {
*/
public static Class<?> getMinecraftServerClass() {
try {
return getMinecraftClass("MinecraftServer");
return getMinecraftClass("server.MinecraftServer","MinecraftServer");
} catch (RuntimeException e) {
useFallbackServer();
@@ -958,7 +975,7 @@ public class MinecraftReflection {
*/
public static Class<?> getStatisticClass() {
// TODO: Implement fallback
return getMinecraftClass("Statistic");
return getMinecraftClass("stats.Statistic", "Statistic");
}
/**
@@ -967,7 +984,7 @@ public class MinecraftReflection {
*/
public static Class<?> getStatisticListClass() {
// TODO: Implement fallback
return getMinecraftClass("StatisticList");
return getMinecraftClass("stats.StatisticList", "StatisticList");
}
/**
@@ -994,7 +1011,8 @@ public class MinecraftReflection {
*/
public static Class<?> getPlayerListClass() {
try {
return getMinecraftClass("ServerConfigurationManager", "PlayerList");
return getMinecraftClass("server.players.PlayerList",
"ServerConfigurationManager", "PlayerList");
} catch (RuntimeException e) {
useFallbackServer();
@@ -1033,7 +1051,7 @@ public class MinecraftReflection {
*/
public static Class<?> getPlayerConnectionClass() {
try {
return getMinecraftClass("PlayerConnection", "NetServerHandler");
return getMinecraftClass("server.network.PlayerConnection","PlayerConnection", "NetServerHandler");
} catch (RuntimeException e) {
try {
// Use the player connection field
@@ -1077,7 +1095,7 @@ public class MinecraftReflection {
*/
public static Class<?> getNetworkManagerClass() {
try {
return getMinecraftClass("INetworkManager", "NetworkManager");
return getMinecraftClass("network.NetworkManager", "INetworkManager", "NetworkManager");
} catch (RuntimeException e) {
Constructor<?> selected = FuzzyReflection.fromClass(getPlayerConnectionClass()).
getConstructor(FuzzyMethodContract.newBuilder().
@@ -1110,7 +1128,7 @@ public class MinecraftReflection {
*/
public static Class<?> getItemStackClass() {
try {
return getMinecraftClass("ItemStack");
return getMinecraftClass("world.item.ItemStack", "ItemStack");
} catch (RuntimeException e) {
// Use the handle reference
return setMinecraftClass("ItemStack",
@@ -1124,7 +1142,7 @@ public class MinecraftReflection {
*/
public static Class<?> getBlockClass() {
try {
return getMinecraftClass("Block");
return getMinecraftClass("world.level.block.Block", "Block");
} catch (RuntimeException e) {
FuzzyReflection reflect = FuzzyReflection.fromClass(getItemStackClass());
Set<Class<?>> candidates = new HashSet<Class<?>>();
@@ -1149,15 +1167,15 @@ public class MinecraftReflection {
}
public static Class<?> getItemClass() {
return getNullableNMS("Item");
return getNullableNMS("world.item.Item", "Item");
}
public static Class<?> getFluidTypeClass() {
return getNullableNMS("FluidType");
return getNullableNMS("world.level.material.FluidType", "FluidType");
}
public static Class<?> getParticleTypeClass() {
return getNullableNMS("ParticleType");
return getNullableNMS("core.particles.ParticleType", "ParticleType");
}
/**
@@ -1188,7 +1206,7 @@ public class MinecraftReflection {
*/
public static Class<?> getDataWatcherClass() {
try {
return getMinecraftClass("DataWatcher");
return getMinecraftClass("network.syncher.DataWatcher", "DataWatcher");
} catch (RuntimeException e) {
// Describe the DataWatcher
FuzzyClassContract dataWatcherContract = FuzzyClassContract.newBuilder().
@@ -1236,7 +1254,7 @@ public class MinecraftReflection {
*/
public static Class<?> getBlockPositionClass() {
try {
return getMinecraftClass("BlockPosition");
return getMinecraftClass("core.BlockPosition", "BlockPosition");
} catch (RuntimeException e) {
try {
Class<?> normalChunkGenerator = getCraftBukkitClass("generator.NormalChunkGenerator");
@@ -1263,7 +1281,7 @@ public class MinecraftReflection {
*/
public static Class<?> getVec3DClass() {
try {
return getMinecraftClass("Vec3D");
return getMinecraftClass("world.phys.Vec3D", "Vec3D");
} catch (RuntimeException e) {
// TODO: Figure out a fuzzy field contract
return null;
@@ -1288,13 +1306,10 @@ public class MinecraftReflection {
* @return The ChunkCoordIntPair class.
*/
public static Class<?> getChunkCoordIntPair() {
if (!isUsingNetty())
throw new IllegalArgumentException("Not supported on 1.6.4 and older.");
try {
return getMinecraftClass("ChunkCoordIntPair");
return getMinecraftClass("world.level.ChunkCoordIntPair", "ChunkCoordIntPair");
} catch (RuntimeException e) {
Class<?> packet = PacketRegistry.getPacketClassFromType(PacketType.Play.Server.MULTI_BLOCK_CHANGE);
Class<?> packet = PacketType.Play.Server.MULTI_BLOCK_CHANGE.getPacketClass();
AbstractFuzzyMatcher<Class<?>> chunkCoordIntContract = FuzzyClassContract.newBuilder().
field(FuzzyFieldContract.newBuilder().
@@ -1326,7 +1341,8 @@ public class MinecraftReflection {
*/
public static Class<?> getDataWatcherItemClass() {
try {
return getMinecraftClass("DataWatcher$Item", "DataWatcher$WatchableObject", "WatchableObject");
return getMinecraftClass("network.syncher.DataWatcher$Item",
"DataWatcher$Item", "DataWatcher$WatchableObject", "WatchableObject");
} catch (RuntimeException e) {
Method selected = FuzzyReflection.fromClass(getDataWatcherClass(), true).
getMethod(FuzzyMethodContract.newBuilder().
@@ -1342,7 +1358,7 @@ public class MinecraftReflection {
public static Class<?> getDataWatcherObjectClass() {
try {
return getMinecraftClass("DataWatcherObject");
return getMinecraftClass("network.syncher.DataWatcherObject", "DataWatcherObject");
} catch (RuntimeException ex) {
return null;
}
@@ -1362,27 +1378,27 @@ public class MinecraftReflection {
public static Class<?> getDataWatcherSerializerClass() {
// TODO Implement a fallback
return getNullableNMS("DataWatcherSerializer");
return getNullableNMS("network.syncher.DataWatcherSerializer", "DataWatcherSerializer");
}
public static Class<?> getDataWatcherRegistryClass() {
// TODO Implement a fallback
return getMinecraftClass("DataWatcherRegistry");
return getMinecraftClass("network.syncher.DataWatcherRegistry", "DataWatcherRegistry");
}
public static Class<?> getMinecraftKeyClass() {
// TODO Implement a fallback
return getMinecraftClass("MinecraftKey");
return getMinecraftClass("resources.MinecraftKey", "MinecraftKey");
}
public static Class<?> getMobEffectListClass() {
// TODO Implement a fallback
return getMinecraftClass("MobEffectList");
return getMinecraftClass("world.effect.MobEffectList", "MobEffectList");
}
public static Class<?> getSoundEffectClass() {
try {
return getMinecraftClass("SoundEffect");
return getMinecraftClass("sounds.SoundEffect", "SoundEffect");
} catch (RuntimeException ex) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(PacketType.Play.Server.NAMED_SOUND_EFFECT.getPacketClass(), true);
Field field = fuzzy.getFieldByType("(.*)(Sound)(.*)");
@@ -1396,7 +1412,7 @@ public class MinecraftReflection {
*/
public static Class<?> getServerConnectionClass() {
try {
return getMinecraftClass("ServerConnection");
return getMinecraftClass("server.network.ServerConnection", "ServerConnection");
} catch (RuntimeException e) {
Method selected = null;
FuzzyClassContract.Builder serverConnectionContract = FuzzyClassContract.newBuilder().
@@ -1441,7 +1457,7 @@ public class MinecraftReflection {
*/
public static Class<?> getNBTBaseClass() {
try {
return getMinecraftClass("NBTBase");
return getMinecraftClass("nbt.NBTBase", "NBTBase");
} catch (RuntimeException e) {
Class<?> nbtBase = null;
@@ -1501,7 +1517,7 @@ public class MinecraftReflection {
* @return The NBT read limiter.
*/
public static Class<?> getNBTReadLimiterClass() {
return getMinecraftClass("NBTReadLimiter");
return getMinecraftClass("nbt.NBTReadLimiter", "NBTReadLimiter");
}
/**
@@ -1510,7 +1526,7 @@ public class MinecraftReflection {
*/
public static Class<?> getNBTCompoundClass() {
try {
return getMinecraftClass("NBTTagCompound");
return getMinecraftClass("nbt.NBTTagCompound","NBTTagCompound");
} catch (RuntimeException e) {
return setMinecraftClass(
"NBTTagCompound",
@@ -1525,7 +1541,8 @@ public class MinecraftReflection {
*/
public static Class<?> getEntityTrackerClass() {
try {
return getMinecraftClass("EntityTracker", "PlayerChunkMap$EntityTracker");
return getMinecraftClass("server.level.PlayerChunkMap$EntityTracker",
"EntityTracker", "PlayerChunkMap$EntityTracker");
} catch (RuntimeException e) {
FuzzyClassContract entityTrackerContract = FuzzyClassContract.newBuilder().
field(FuzzyFieldContract.newBuilder().
@@ -1553,38 +1570,6 @@ public class MinecraftReflection {
}
}
/**
* Retrieve the NetworkListenThread class (NMS).
* <p>
* Note that this class was removed after Minecraft 1.3.1.
* @return NetworkListenThread class.
*/
public static Class<?> getNetworkListenThreadClass() {
try {
return getMinecraftClass("NetworkListenThread");
} catch (RuntimeException e) {
FuzzyClassContract networkListenContract = FuzzyClassContract.newBuilder().
field(FuzzyFieldContract.newBuilder().
typeDerivedOf(ServerSocket.class)).
field(FuzzyFieldContract.newBuilder().
typeDerivedOf(Thread.class)).
field(FuzzyFieldContract.newBuilder().
typeDerivedOf(List.class)).
method(FuzzyMethodContract.newBuilder().
parameterExactType(getPlayerConnectionClass())).
build();
Field selected = FuzzyReflection.fromClass(MinecraftReflection.getMinecraftServerClass(), true).
getField(FuzzyFieldContract.newBuilder().
typeMatches(networkListenContract).
build()
);
// Go by the defined type of this field
return setMinecraftClass("NetworkListenThread", selected.getType());
}
}
/**
* Retrieve the attribute snapshot class.
* <p>
@@ -1593,7 +1578,8 @@ public class MinecraftReflection {
*/
public static Class<?> getAttributeSnapshotClass() {
try {
return getMinecraftClass("AttributeSnapshot", "PacketPlayOutUpdateAttributes$AttributeSnapshot");
return getMinecraftClass("network.protocol.game.PacketPlayOutUpdateAttributes$AttributeSnapshot",
"AttributeSnapshot", "PacketPlayOutUpdateAttributes$AttributeSnapshot");
} catch (RuntimeException ex) {
try {
// It should be the parameter of a list in the update attributes packet
@@ -1652,7 +1638,7 @@ public class MinecraftReflection {
*/
public static Class<?> getAttributeModifierClass() {
try {
return getMinecraftClass("AttributeModifier");
return getMinecraftClass("world.entity.ai.attributes.AttributeModifier", "AttributeModifier");
} catch (RuntimeException e) {
getAttributeSnapshotClass();
@@ -1668,10 +1654,10 @@ public class MinecraftReflection {
*/
public static Class<?> getMobEffectClass() {
try {
return getMinecraftClass("MobEffect");
return getMinecraftClass("world.effect.MobEffect", "MobEffect");
} catch (RuntimeException e) {
// It is the second parameter in Packet41MobEffect
Class<?> packet = PacketRegistry.getPacketClassFromType(PacketType.Play.Server.ENTITY_EFFECT);
Class<?> packet = PacketType.Play.Server.ENTITY_EFFECT.getPacketClass();
Constructor<?> constructor = FuzzyReflection.fromClass(packet).getConstructor(
FuzzyMethodContract.newBuilder().
parameterCount(2).
@@ -1689,7 +1675,7 @@ public class MinecraftReflection {
*/
public static Class<?> getPacketDataSerializerClass() {
try {
return getMinecraftClass("PacketDataSerializer");
return getMinecraftClass("network.PacketDataSerializer", "PacketDataSerializer");
} catch (RuntimeException e) {
Class<?> packet = getPacketClass();
Method method = FuzzyReflection.fromClass(packet).getMethod(
@@ -1709,7 +1695,7 @@ public class MinecraftReflection {
*/
public static Class<?> getNbtCompressedStreamToolsClass() {
try {
return getMinecraftClass("NBTCompressedStreamTools");
return getMinecraftClass("nbt.NBTCompressedStreamTools", "NBTCompressedStreamTools");
} catch (RuntimeException e) {
Class<?> packetSerializer = getPacketDataSerializerClass();
@@ -1738,7 +1724,7 @@ public class MinecraftReflection {
* @return The tile entity class.
*/
public static Class<?> getTileEntityClass() {
return getMinecraftClass("TileEntity");
return getMinecraftClass("world.level.block.entity.TileEntity", "TileEntity");
}
/**
@@ -1820,7 +1806,54 @@ public class MinecraftReflection {
* @return The PlayerInfoData class
*/
public static Class<?> getPlayerInfoDataClass() {
return getMinecraftClass("PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
return getMinecraftClass("network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData",
"PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
}
/**
* Retrieves the entity use action class in 1.17.
* @return The EntityUseAction class
*/
public static Class<?> getEnumEntityUseActionClass() {
Class<?> packetClass = PacketType.Play.Client.USE_ENTITY.getPacketClass();
return FuzzyReflection.fromClass(packetClass, true).getFieldByType("^.*(EnumEntityUseAction)").getType();
}
/**
* Get a method accessor to get the actual use action out of the wrapping EnumEntityUseAction in 1.17.
* @return a method accessor to get the actual use action
*/
public static MethodAccessor getEntityUseActionEnumMethodAccessor() {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
return Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.returnTypeExact(EnumWrappers.getEntityUseActionClass())
.build()));
}
/**
* Get a field accessor for the hand in the wrapping EnumEntityUseAction in 1.17.
*
* @param enumEntityUseAction the object instance of the action, the field is not present in attack.
* @return a field accessor for the hand in the wrapping EnumEntityUseAction
*/
public static FieldAccessor getHandEntityUseActionEnumFieldAccessor(Object enumEntityUseAction) {
FuzzyReflection fuzzy = FuzzyReflection.fromObject(enumEntityUseAction, true);
return Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
.typeExact(EnumWrappers.getHandClass())
.build()));
}
/**
* Get a field accessor for the vec3d in the wrapping EnumEntityUseAction in 1.17.
*
* @param enumEntityUseAction the object instance of the action, the field is not present in attack.
* @return a field accessor for the hand in the wrapping EnumEntityUseAction
*/
public static FieldAccessor getVec3EntityUseActionEnumFieldAccessor(Object enumEntityUseAction) {
FuzzyReflection fuzzy = FuzzyReflection.fromObject(enumEntityUseAction, true);
return Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
.typeExact(MinecraftReflection.getVec3DClass())
.build()));
}
/**
@@ -1837,7 +1870,7 @@ public class MinecraftReflection {
* @return The IBlockData class
*/
public static Class<?> getIBlockDataClass() {
return getMinecraftClass("IBlockData");
return getMinecraftClass("world.level.block.state.IBlockData", "IBlockData");
}
/**
@@ -1856,16 +1889,35 @@ public class MinecraftReflection {
return getArrayClass(getMultiBlockChangeInfoClass());
}
/**
* Retrieve the PacketPlayOutGameStateChange.a class, aka GameState in 1.16
* @return The GameState class
*/
public static Class<?> getGameStateClass() {
// it's called "a" so there's not a whole lot we can do to identify it
Class<?> packetClass = PacketType.Play.Server.GAME_STATE_CHANGE.getPacketClass();
return packetClass.getClasses()[0];
}
public static boolean signUpdateExists() {
try {
return getMinecraftClass("PacketPlayOutUpdateSign") != null;
} catch (RuntimeException ex) {
return false;
}
return getNullableNMS("PacketPlayOutUpdateSign") != null;
}
public static Class<?> getNonNullListClass() {
return getMinecraftClass("NonNullList");
return getMinecraftClass("core.NonNullList", "NonNullList");
}
public static MethodAccessor getNonNullListCreateAccessor() {
try {
Class<?> nonNullListType = MinecraftReflection.getNonNullListClass();
return Accessors.getMethodAccessor(FuzzyReflection.fromClass(nonNullListType).getMethod(
FuzzyMethodContract.newBuilder()
.returnTypeExact(nonNullListType)
.requireModifier(Modifier.STATIC)
.build()));
} catch (Exception ex) {
return null;
}
}
public static Class<?> getCraftSoundClass() {
@@ -1873,7 +1925,7 @@ public class MinecraftReflection {
}
public static Class<?> getSectionPositionClass() {
return getMinecraftClass("SectionPosition");
return getMinecraftClass("core.SectionPosition", "SectionPosition");
}
// ---- ItemStack conversions
@@ -2027,10 +2079,12 @@ public class MinecraftReflection {
.orElseThrow(() -> new RuntimeException("Failed to find NMS class: " + className));
}
public static Class<?> getNullableNMS(String className) {
if (minecraftPackage == null)
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
return minecraftPackage.getPackageClass(className).orElse(null);
public static Class<?> getNullableNMS(String className, String... aliases) {
try {
return getMinecraftClass(className, aliases);
} catch (RuntimeException ex) {
return null;
}
}
/**
@@ -2163,4 +2217,74 @@ public class MinecraftReflection {
throw new RuntimeException("Cannot construct packet serializer.", e);
}
}
public static Class<?> getNbtTagTypes() {
return getMinecraftClass("nbt.NBTTagTypes", "NBTTagTypes");
}
public static Class<?> getChatDeserializer() {
return getMinecraftClass("util.ChatDeserializer", "ChatDeserializer");
}
public static Class<?> getDimensionManager() {
return getMinecraftClass("world.level.dimension.DimensionManager", "DimensionManager");
}
public static Class<?> getMerchantRecipeList() {
return getMinecraftClass("world.item.trading.MerchantRecipeList", "MerchantRecipeList");
}
public static Class<?> getResourceKey() {
return getMinecraftClass("resources.ResourceKey", "ResourceKey");
}
public static Class<?> getEntityTypes() {
return getMinecraftClass("world.entity.EntityTypes", "EntityTypes");
}
public static Class<?> getParticleParam() {
return getMinecraftClass("core.particles.ParticleParam", "ParticleParam");
}
public static Class<?> getSectionPosition() {
return getMinecraftClass("core.SectionPosition", "SectionPosition");
}
public static Class<?> getChunkProviderServer() {
return getMinecraftClass("server.level.ChunkProviderServer", "ChunkProviderServer");
}
public static Class<?> getPlayerChunkMap() {
return getMinecraftClass("server.level.PlayerChunkMap", "PlayerChunkMap");
}
public static Class<?> getIRegistry() {
return getNullableNMS("core.IRegistry", "IRegistry");
}
public static Class<?> getAttributeBase() {
return getMinecraftClass("world.entity.ai.attributes.AttributeBase", "AttributeBase");
}
public static Class<?> getFastUtilClass(String className) {
return getLibraryClass("it.unimi.dsi.fastutil." + className);
}
public static Class<?> getInt2ObjectMapClass() {
return getFastUtilClass("ints.Int2ObjectMap");
}
public static Class<?> getIntArrayListClass() {
return getFastUtilClass("ints.IntArrayList");
}
public static Class<?> getLibraryClass(String classname) {
try {
return getMinecraftLibraryClass(classname);
} catch (RuntimeException ex) {
Class<?> clazz = getMinecraftLibraryClass("org.bukkit.craftbukkit.libs." + classname);
setMinecraftLibraryClass(classname, clazz);
return clazz;
}
}
}
@@ -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\\-.]+).*");
/**
* Version 1.18 - caves and cliffs part 2
*/
public static final MinecraftVersion CAVES_CLIFFS_2 = new MinecraftVersion("1.18");
/**
* Version 1.17 - caves and cliffs part 1
*/
public static final MinecraftVersion CAVES_CLIFFS_1 = new MinecraftVersion("1.17");
/**
* Version 1.16.2 - breaking change to the nether update
*/
@@ -0,0 +1,87 @@
package com.comphenix.protocol.utility;
import com.comphenix.protocol.ProtocolLibrary;
import io.netty.util.Version;
import java.util.Map;
public class NettyVersion {
private final static String NETTY_COMMON_ID = "netty-common";
private final static String NETTY_ALL_ID = "netty-all";
private static NettyVersion version;
public static NettyVersion getVersion() {
if(version == null) {
version = detectVersion();
}
return version;
}
private static NettyVersion detectVersion() {
Map<String, Version> nettyArtifacts = Version.identify();
Version version = nettyArtifacts.get(NETTY_COMMON_ID);
if(version == null) {
version = nettyArtifacts.get(NETTY_ALL_ID);
}
if(version != null) {
return new NettyVersion(version.artifactVersion());
}
return new NettyVersion(null);
}
private boolean valid = false;
private int major, minor, revision;
public NettyVersion(String s) {
if(s == null) {
return;
}
String[] split = s.split( "\\.");
try {
this.major = Integer.parseInt(split[0]);
this.minor = Integer.parseInt(split[1]);
this.revision = Integer.parseInt(split[2]);
this.valid = true;
} catch (Throwable t) {
ProtocolLibrary.getPlugin().getLogger().warning("Could not detect netty version: '" + s + "'");
}
}
@Override
public String toString() {
if(!valid) {
return "(invalid)";
}
return major + "." + minor + "." + revision;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof NettyVersion)) {
return false;
}
NettyVersion v = (NettyVersion) obj;
return v.major == major && v.minor == minor && v.revision == revision;
}
public int getMajor() {
return major;
}
public int getMinor() {
return minor;
}
public int getRevision() {
return revision;
}
public boolean isValid() {
return this.valid;
}
public boolean isGreaterThan(int major, int minor, int rev) {
return this.major > major || this.minor > minor || this.revision > rev;
}
}
@@ -0,0 +1,741 @@
package com.comphenix.protocol.utility;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufProcessor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.charset.Charset;
public class ZeroBuffer extends ByteBuf {
@Override
public int capacity() {
return 0;
}
@Override
public ByteBuf capacity(int i) {
return null;
}
@Override
public int maxCapacity() {
return 0;
}
@Override
public ByteBufAllocator alloc() {
return null;
}
@Override
public ByteOrder order() {
return null;
}
@Override
public ByteBuf order(ByteOrder byteOrder) {
return null;
}
@Override
public ByteBuf unwrap() {
return null;
}
@Override
public boolean isDirect() {
return false;
}
@Override
public int readerIndex() {
return 0;
}
@Override
public ByteBuf readerIndex(int i) {
return null;
}
@Override
public int writerIndex() {
return 0;
}
@Override
public ByteBuf writerIndex(int i) {
return null;
}
@Override
public ByteBuf setIndex(int i, int i1) {
return null;
}
@Override
public int readableBytes() {
return 0;
}
@Override
public int writableBytes() {
return 0;
}
@Override
public int maxWritableBytes() {
return 0;
}
@Override
public boolean isReadable() {
return false;
}
@Override
public boolean isReadable(int i) {
return false;
}
@Override
public boolean isWritable() {
return false;
}
@Override
public boolean isWritable(int i) {
return false;
}
@Override
public ByteBuf clear() {
return null;
}
@Override
public ByteBuf markReaderIndex() {
return null;
}
@Override
public ByteBuf resetReaderIndex() {
return null;
}
@Override
public ByteBuf markWriterIndex() {
return null;
}
@Override
public ByteBuf resetWriterIndex() {
return null;
}
@Override
public ByteBuf discardReadBytes() {
return null;
}
@Override
public ByteBuf discardSomeReadBytes() {
return null;
}
@Override
public ByteBuf ensureWritable(int i) {
return null;
}
@Override
public int ensureWritable(int i, boolean b) {
return 0;
}
@Override
public boolean getBoolean(int i) {
return false;
}
@Override
public byte getByte(int i) {
return 0;
}
@Override
public short getUnsignedByte(int i) {
return 0;
}
@Override
public short getShort(int i) {
return 0;
}
@Override
public int getUnsignedShort(int i) {
return 0;
}
@Override
public int getMedium(int i) {
return 0;
}
@Override
public int getUnsignedMedium(int i) {
return 0;
}
@Override
public int getInt(int i) {
return 0;
}
@Override
public long getUnsignedInt(int i) {
return 0;
}
@Override
public long getLong(int i) {
return 0;
}
@Override
public char getChar(int i) {
return 0;
}
@Override
public float getFloat(int i) {
return 0;
}
@Override
public double getDouble(int i) {
return 0;
}
@Override
public ByteBuf getBytes(int i, ByteBuf byteBuf) {
return null;
}
@Override
public ByteBuf getBytes(int i, ByteBuf byteBuf, int i1) {
return null;
}
@Override
public ByteBuf getBytes(int i, ByteBuf byteBuf, int i1, int i2) {
return null;
}
@Override
public ByteBuf getBytes(int i, byte[] bytes) {
return null;
}
@Override
public ByteBuf getBytes(int i, byte[] bytes, int i1, int i2) {
return null;
}
@Override
public ByteBuf getBytes(int i, ByteBuffer byteBuffer) {
return null;
}
@Override
public ByteBuf getBytes(int i, OutputStream outputStream, int i1) throws IOException {
return null;
}
@Override
public int getBytes(int i, GatheringByteChannel gatheringByteChannel, int i1) throws IOException {
return 0;
}
@Override
public ByteBuf setBoolean(int i, boolean b) {
return null;
}
@Override
public ByteBuf setByte(int i, int i1) {
return null;
}
@Override
public ByteBuf setShort(int i, int i1) {
return null;
}
@Override
public ByteBuf setMedium(int i, int i1) {
return null;
}
@Override
public ByteBuf setInt(int i, int i1) {
return null;
}
@Override
public ByteBuf setLong(int i, long l) {
return null;
}
@Override
public ByteBuf setChar(int i, int i1) {
return null;
}
@Override
public ByteBuf setFloat(int i, float v) {
return null;
}
@Override
public ByteBuf setDouble(int i, double v) {
return null;
}
@Override
public ByteBuf setBytes(int i, ByteBuf byteBuf) {
return null;
}
@Override
public ByteBuf setBytes(int i, ByteBuf byteBuf, int i1) {
return null;
}
@Override
public ByteBuf setBytes(int i, ByteBuf byteBuf, int i1, int i2) {
return null;
}
@Override
public ByteBuf setBytes(int i, byte[] bytes) {
return null;
}
@Override
public ByteBuf setBytes(int i, byte[] bytes, int i1, int i2) {
return null;
}
@Override
public ByteBuf setBytes(int i, ByteBuffer byteBuffer) {
return null;
}
@Override
public int setBytes(int i, InputStream inputStream, int i1) throws IOException {
return 0;
}
@Override
public int setBytes(int i, ScatteringByteChannel scatteringByteChannel, int i1) throws IOException {
return 0;
}
@Override
public ByteBuf setZero(int i, int i1) {
return null;
}
@Override
public boolean readBoolean() {
return false;
}
@Override
public byte readByte() {
return 0;
}
@Override
public short readUnsignedByte() {
return 0;
}
@Override
public short readShort() {
return 0;
}
@Override
public int readUnsignedShort() {
return 0;
}
@Override
public int readMedium() {
return 0;
}
@Override
public int readUnsignedMedium() {
return 0;
}
@Override
public int readInt() {
return 0;
}
@Override
public long readUnsignedInt() {
return 0;
}
@Override
public long readLong() {
return 0;
}
@Override
public char readChar() {
return 0;
}
@Override
public float readFloat() {
return 0;
}
@Override
public double readDouble() {
return 0;
}
@Override
public ByteBuf readBytes(int i) {
return null;
}
@Override
public ByteBuf readSlice(int i) {
return null;
}
@Override
public ByteBuf readBytes(ByteBuf byteBuf) {
return null;
}
@Override
public ByteBuf readBytes(ByteBuf byteBuf, int i) {
return null;
}
@Override
public ByteBuf readBytes(ByteBuf byteBuf, int i, int i1) {
return null;
}
@Override
public ByteBuf readBytes(byte[] bytes) {
return null;
}
@Override
public ByteBuf readBytes(byte[] bytes, int i, int i1) {
return null;
}
@Override
public ByteBuf readBytes(ByteBuffer byteBuffer) {
return null;
}
@Override
public ByteBuf readBytes(OutputStream outputStream, int i) throws IOException {
return null;
}
@Override
public int readBytes(GatheringByteChannel gatheringByteChannel, int i) throws IOException {
return 0;
}
@Override
public ByteBuf skipBytes(int i) {
return null;
}
@Override
public ByteBuf writeBoolean(boolean b) {
return null;
}
@Override
public ByteBuf writeByte(int i) {
return null;
}
@Override
public ByteBuf writeShort(int i) {
return null;
}
@Override
public ByteBuf writeMedium(int i) {
return null;
}
@Override
public ByteBuf writeInt(int i) {
return null;
}
@Override
public ByteBuf writeLong(long l) {
return null;
}
@Override
public ByteBuf writeChar(int i) {
return null;
}
@Override
public ByteBuf writeFloat(float v) {
return null;
}
@Override
public ByteBuf writeDouble(double v) {
return null;
}
@Override
public ByteBuf writeBytes(ByteBuf byteBuf) {
return null;
}
@Override
public ByteBuf writeBytes(ByteBuf byteBuf, int i) {
return null;
}
@Override
public ByteBuf writeBytes(ByteBuf byteBuf, int i, int i1) {
return null;
}
@Override
public ByteBuf writeBytes(byte[] bytes) {
return null;
}
@Override
public ByteBuf writeBytes(byte[] bytes, int i, int i1) {
return null;
}
@Override
public ByteBuf writeBytes(ByteBuffer byteBuffer) {
return null;
}
@Override
public int writeBytes(InputStream inputStream, int i) throws IOException {
return 0;
}
@Override
public int writeBytes(ScatteringByteChannel scatteringByteChannel, int i) throws IOException {
return 0;
}
@Override
public ByteBuf writeZero(int i) {
return null;
}
@Override
public int indexOf(int i, int i1, byte b) {
return 0;
}
@Override
public int bytesBefore(byte b) {
return 0;
}
@Override
public int bytesBefore(int i, byte b) {
return 0;
}
@Override
public int bytesBefore(int i, int i1, byte b) {
return 0;
}
@Override
public int forEachByte(ByteBufProcessor byteBufProcessor) {
return 0;
}
@Override
public int forEachByte(int i, int i1, ByteBufProcessor byteBufProcessor) {
return 0;
}
@Override
public int forEachByteDesc(ByteBufProcessor byteBufProcessor) {
return 0;
}
@Override
public int forEachByteDesc(int i, int i1, ByteBufProcessor byteBufProcessor) {
return 0;
}
@Override
public ByteBuf copy() {
return null;
}
@Override
public ByteBuf copy(int i, int i1) {
return null;
}
@Override
public ByteBuf slice() {
return null;
}
@Override
public ByteBuf slice(int i, int i1) {
return null;
}
@Override
public ByteBuf duplicate() {
return null;
}
@Override
public int nioBufferCount() {
return 0;
}
@Override
public ByteBuffer nioBuffer() {
return null;
}
@Override
public ByteBuffer nioBuffer(int i, int i1) {
return null;
}
@Override
public ByteBuffer internalNioBuffer(int i, int i1) {
return null;
}
@Override
public ByteBuffer[] nioBuffers() {
return new ByteBuffer[0];
}
@Override
public ByteBuffer[] nioBuffers(int i, int i1) {
return new ByteBuffer[0];
}
@Override
public boolean hasArray() {
return false;
}
@Override
public byte[] array() {
return new byte[0];
}
@Override
public int arrayOffset() {
return 0;
}
@Override
public boolean hasMemoryAddress() {
return false;
}
@Override
public long memoryAddress() {
return 0;
}
@Override
public String toString(Charset charset) {
return "";
}
@Override
public String toString(int i, int i1, Charset charset) {
return "";
}
@Override
public int hashCode() {
return 0;
}
@Override
public boolean equals(Object o) {
return false;
}
@Override
public int compareTo(ByteBuf byteBuf) {
return 0;
}
@Override
public String toString() {
return null;
}
@Override
public ByteBuf retain(int i) {
return null;
}
@Override
public boolean release() {
return false;
}
@Override
public boolean release(int i) {
return false;
}
@Override
public int refCnt() {
return 0;
}
@Override
public ByteBuf retain() {
return null;
}
}
@@ -0,0 +1,57 @@
/**
* 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.wrappers;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
/**
* Utility class for converting between the Adventure API Component and ProtocolLib's wrapper
* <p>
* Note: The Adventure API Component is not included in CraftBukkit, Bukkit or Spigot and but is present in PaperMC.
*/
public class AdventureComponentConverter {
private AdventureComponentConverter() {
}
/**
* Converts a {@link WrappedChatComponent} into a {@link Component}
* @param wrapper ProtocolLib wrapper
* @return Component
*/
public static Component fromWrapper(WrappedChatComponent wrapper) {
return GsonComponentSerializer.gson().deserialize(wrapper.getJson());
}
/**
* Converts a {@link Component} into a ProtocolLib wrapper
* @param components Component
* @return ProtocolLib wrapper
*/
public static WrappedChatComponent fromComponent(Component component) {
return WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(component));
}
public static Class<?> getComponentClass() {
return Component.class;
}
public static Component clone(Object component) {
return (Component) component;
}
}
@@ -16,6 +16,12 @@
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.google.common.base.Defaults;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
@@ -41,9 +47,18 @@ import com.comphenix.protocol.utility.MinecraftReflection;
* @author dmulloy2
*/
public class AutoWrapper<T> implements EquivalentConverter<T> {
private static final Object[] NO_ARGS = new Object[0];
private Map<Integer, Function<Object, Object>> wrappers = new HashMap<>();
private Map<Integer, Function<Object, Object>> unwrappers = new HashMap<>();
// lazy
private FieldAccessor[] nmsAccessors;
private FieldAccessor[] wrapperAccessors;
private Object[] nmsDefaultArgs;
private ConstructorAccessor nmsInstanceCreator;
private Class<T> wrapperClass;
private Class<?> nmsClass;
@@ -79,75 +94,77 @@ public class AutoWrapper<T> implements EquivalentConverter<T> {
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);
// ensures that all accessors are present
computeFieldAccessors();
for (int i = 0; i < wrapperFields.length; i++) {
try {
Field wrapperField = wrapperFields[i];
for (int i = 0; i < wrapperAccessors.length; i++) {
FieldAccessor source = nmsAccessors[i];
FieldAccessor target = wrapperAccessors[i];
Field nmsField = nmsFields[i];
if (!nmsField.isAccessible())
nmsField.setAccessible(true);
Object value = source.get(nmsObject);
if (wrappers.containsKey(i))
value = wrappers.get(i).apply(value);
Object value = nmsField.get(nmsObject);
if (wrappers.containsKey(i))
value = wrappers.get(i).apply(value);
wrapperField.set(instance, value);
} catch (Exception ex) {
throw new InvalidWrapperException("Failed to wrap field at index " + i, ex);
}
target.set(instance, value);
}
return instance;
}
public Object unwrap(Object wrapper) {
Object instance;
// ensures that all accessors are present
computeFieldAccessors();
computeNmsConstructorAccess();
try {
instance = nmsClass.newInstance();
} catch (ReflectiveOperationException ex) {
throw new InvalidWrapperException("Failed to construct new " + nmsClass.getSimpleName(), ex);
}
Object instance = nmsInstanceCreator.invoke(nmsDefaultArgs);
Field[] wrapperFields = wrapperClass.getDeclaredFields();
Field[] nmsFields = Arrays
.stream(nmsClass.getDeclaredFields())
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.toArray(Field[]::new);
for (int i = 0; i < wrapperAccessors.length; i++) {
FieldAccessor source = wrapperAccessors[i];
FieldAccessor target = nmsAccessors[i];
for (int i = 0; i < wrapperFields.length; i++) {
try {
Field wrapperField = wrapperFields[i];
Object value = source.get(wrapper);
if (unwrappers.containsKey(i))
value = unwrappers.get(i).apply(value);
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);
}
target.set(instance, value);
}
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);
private void computeFieldAccessors() {
if (nmsAccessors == null) {
nmsAccessors = Arrays
.stream(nmsClass.getDeclaredFields())
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.map(field -> Accessors.getFieldAccessor(field, true))
.toArray(FieldAccessor[]::new);
}
if (wrapperAccessors == null) {
wrapperAccessors = Arrays
.stream(wrapperClass.getDeclaredFields())
.map(field -> Accessors.getFieldAccessor(field, true))
.toArray(FieldAccessor[]::new);
}
}
private void computeNmsConstructorAccess() {
if (nmsInstanceCreator == null) {
ConstructorAccessor noArgs = Accessors.getConstructorAccessorOrNull(nmsClass);
if (noArgs != null) {
// no args constructor is available - use it
nmsInstanceCreator = noArgs;
nmsDefaultArgs = NO_ARGS;
} else {
// use the first constructor of the class
nmsInstanceCreator = Accessors.getConstructorAccessor(nmsClass.getDeclaredConstructors()[0]);
nmsDefaultArgs = Arrays
.stream(nmsInstanceCreator.getConstructor().getParameterTypes())
.map(type -> type.isPrimitive() ? Defaults.defaultValue(type) : null)
.toArray(Object[]::new);
}
}
}
// ---- Equivalent conversion
@@ -17,12 +17,16 @@
package com.comphenix.protocol.wrappers;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -42,6 +46,7 @@ import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
@@ -49,12 +54,17 @@ import com.comphenix.protocol.wrappers.EnumWrappers.Dimension;
import com.comphenix.protocol.wrappers.EnumWrappers.FauxEnumConverter;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.bukkit.*;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.advancement.Advancement;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
@@ -257,6 +267,87 @@ public class BukkitConverters {
};
}
private static final Map<Class<?>, Supplier<List<Object>>> LIST_SUPPLIERS = new ConcurrentHashMap<>();
private static <T> Object getGenericList(Class<?> listClass, List<T> specific, EquivalentConverter<T> itemConverter) {
List<Object> newList;
Supplier<List<Object>> supplier = LIST_SUPPLIERS.get(listClass);
if (supplier == null) {
try {
Constructor<?> ctor = listClass.getConstructor();
newList = (List<Object>) ctor.newInstance();
supplier = () -> {
try {
return (List<Object>) ctor.newInstance();
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
};
} catch (ReflectiveOperationException ex) {
// ex.printStackTrace();
supplier = ArrayList::new;
newList = new ArrayList<>();
}
LIST_SUPPLIERS.put(listClass, supplier);
} else {
newList = supplier.get();
}
// Convert each object
for (T position : specific) {
if (position != null) {
Object converted = itemConverter.getGeneric(position);
if (converted != null) {
newList.add(converted);
}
} else {
newList.add(null);
}
}
return newList;
}
private static <T> List<T> getSpecificList(Object generic, EquivalentConverter<T> itemConverter) {
if (generic instanceof Collection) {
List<T> items = new ArrayList<>();
// Copy everything to a new list
for (Object item : (Collection<Object>) generic) {
T result = itemConverter.getSpecific(item);
if (item != null)
items.add(result);
}
return items;
}
// Not valid
return null;
}
public static <T> EquivalentConverter<List<T>> getListConverter(final Class<?> listClass, final EquivalentConverter<T> itemConverter) {
return ignoreNull(new EquivalentConverter<List<T>>() {
@Override
public List<T> getSpecific(Object generic) {
return getSpecificList(generic, itemConverter);
}
@Override
public Object getGeneric(List<T> specific) {
return getGenericList(listClass, specific, itemConverter);
}
@Override
public Class<List<T>> getSpecificType() {
// Damn you Java
Class<?> dummy = List.class;
return (Class<List<T>>) dummy;
}
});
}
/**
* Retrieve an equivalent converter for a list of generic items.
* @param <T> Type
@@ -268,46 +359,12 @@ public class BukkitConverters {
return ignoreNull(new EquivalentConverter<List<T>>() {
@Override
public List<T> getSpecific(Object generic) {
if (generic instanceof Collection) {
List<T> items = new ArrayList<>();
// Copy everything to a new list
for (Object item : (Collection<Object>) generic) {
T result = itemConverter.getSpecific(item);
if (item != null)
items.add(result);
}
return items;
}
// Not valid
return null;
return getSpecificList(generic, itemConverter);
}
@Override
public Object getGeneric(List<T> specific) {
List<Object> newList;
try {
newList = (List<Object>) specific.getClass().newInstance();
} catch (ReflectiveOperationException ex) {
newList = new ArrayList<>();
}
// Convert each object
for (T position : specific) {
if (position != null) {
Object converted = itemConverter.getGeneric(position);
if (converted != null) {
newList.add(converted);
}
} else {
newList.add(null);
}
}
return newList;
return getGenericList(specific.getClass(), specific, itemConverter);
}
@Override
@@ -349,20 +406,6 @@ public class BukkitConverters {
});
}
/**
* @deprecated While this solution is not as abhorrent as I had imagined, I still highly recommend switching to the
* new conversion API.
*/
@Deprecated
public static <T> EquivalentConverter<Set<T>> getSetConverter(final Class<?> genericType,
final EquivalentConverter<T> itemConverter) {
if (itemConverter instanceof EnumWrappers.EnumConverter) {
((EnumWrappers.EnumConverter) itemConverter).setGenericType(genericType);
}
return getSetConverter(itemConverter);
}
/**
* Retrieve an equivalent converter for a set of generic items.
* @param <T> Element type
@@ -674,7 +717,7 @@ public class BukkitConverters {
ProtocolManager manager = managerRef.get();
// Use the entity ID to get a reference to the entity
if (id != null && manager != null) {
if (id != null && id >= 0 && manager != null) {
return manager.getEntityFromID(world, id);
} else {
return null;
@@ -700,7 +743,7 @@ public class BukkitConverters {
@Override
public Object getGeneric(EntityType specific) {
if (entityTypeFromName == null) {
Class<?> entityTypesClass = MinecraftReflection.getMinecraftClass("EntityTypes");
Class<?> entityTypesClass = MinecraftReflection.getEntityTypes();
entityTypeFromName = Accessors.getMethodAccessor(
FuzzyReflection
.fromClass(entityTypesClass, false)
@@ -718,7 +761,7 @@ public class BukkitConverters {
@Override
public EntityType getSpecific(Object generic) {
if (getEntityTypeName == null) {
Class<?> entityTypesClass = MinecraftReflection.getMinecraftClass("EntityTypes");
Class<?> entityTypesClass = MinecraftReflection.getEntityTypes();
getEntityTypeName = Accessors.getMethodAccessor(
FuzzyReflection
.fromClass(entityTypesClass, false)
@@ -1075,7 +1118,7 @@ public class BukkitConverters {
public Advancement getSpecific(Object generic) {
try {
return (Advancement) getCraftBukkitClass("advancement.CraftAdvancement")
.getConstructor(getMinecraftClass("Advancement"))
.getConstructor(getMinecraftClass("advancements.Advancement", "Advancement"))
.newInstance(generic);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
@@ -1194,7 +1237,12 @@ public class BukkitConverters {
public Object getGeneric(PotionEffectType specific) {
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
if (getMobEffect == null) {
getMobEffect = Accessors.getMethodAccessor(clazz, "fromId", int.class);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
getMobEffect = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactArray(int.class)
.returnTypeExact(clazz)
.requireModifier(Modifier.STATIC)
.build()));
}
int id = specific.getId();
@@ -1205,7 +1253,12 @@ public class BukkitConverters {
public PotionEffectType getSpecific(Object generic) {
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
if (getMobEffectId == null) {
getMobEffectId = Accessors.getMethodAccessor(clazz, "getId", clazz);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
getMobEffectId = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactArray(clazz)
.returnTypeExact(int.class)
.requireModifier(Modifier.STATIC)
.build()));
}
int id = (int) getMobEffectId.invoke(null, generic);
@@ -1234,7 +1287,7 @@ public class BukkitConverters {
if (worldKeyField == null) {
Class<?> worldClass = MinecraftReflection.getNmsWorldClass();
Class<?> resourceKeyClass = MinecraftReflection.getMinecraftClass("ResourceKey");
Class<?> resourceKeyClass = MinecraftReflection.getResourceKey();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(nmsWorld.getClass(), true);
worldKeyField = Accessors.getFieldAccessor(fuzzy.getParameterizedField(resourceKeyClass, worldClass));
@@ -1294,14 +1347,95 @@ public class BukkitConverters {
}
}
private static FieldAccessor dimensionKey;
private static MethodAccessor worldHandleAccessor;
private static MethodAccessor worldHandleDimensionManagerAccessor;
public static EquivalentConverter<World> getDimensionConverter() {
return ignoreNull(new EquivalentConverter<World>() {
@Override
public Object getGeneric(World specific) {
return getWorldHandleDimensionManagerAccessor().invoke(getWorldHandleAccessor().invoke(specific));
}
@Override
public World getSpecific(Object generic) {
for (World world : Bukkit.getWorlds()) {
if (getGeneric(world) == generic) {
return world;
}
}
throw new IllegalArgumentException();
}
@Override
public Class<World> getSpecificType() {
return World.class;
}
});
}
private static MethodAccessor getWorldHandleAccessor() {
if (worldHandleAccessor == null) {
Method handleMethod = FuzzyReflection.fromClass(MinecraftReflection.getCraftWorldClass())
.getMethod(FuzzyMethodContract.newBuilder()
.nameExact("getHandle") // i guess this will never change
.returnTypeExact(MinecraftReflection.getWorldServerClass())
.build());
worldHandleAccessor = Accessors.getMethodAccessor(handleMethod);
}
return worldHandleAccessor;
}
private static MethodAccessor getWorldHandleDimensionManagerAccessor() {
if (worldHandleDimensionManagerAccessor == null) {
Method dimensionGetter = FuzzyReflection.fromClass(MinecraftReflection.getWorldServerClass())
.getMethod(FuzzyMethodContract.newBuilder()
.returnTypeExact(MinecraftReflection.getDimensionManager())
.build());
worldHandleDimensionManagerAccessor = Accessors.getMethodAccessor(dimensionGetter);
}
return worldHandleDimensionManagerAccessor;
}
public static EquivalentConverter<Integer> getDimensionIDConverter() {
return ignoreNull(new EquivalentConverter<Integer>() {
@Override
public Object getGeneric(Integer specific) {
if (dimensionManager == null) {
dimensionManager = MinecraftReflection.getNullableNMS("DimensionManager");
dimensionManager = MinecraftReflection.getDimensionManager();
}
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
World world = null;
if (specific == 0) {
world = Bukkit.getWorlds().get(0);
} else if (specific == -1) {
for (World world1 : Bukkit.getWorlds()) {
if (world1.getEnvironment() == World.Environment.NETHER) {
world = world1;
break;
}
}
} else if (specific == 1) {
for (World world1 : Bukkit.getWorlds()) {
if (world1.getEnvironment() == World.Environment.THE_END) {
world = world1;
break;
}
}
}
if (world != null) {
try {
return getWorldHandleDimensionManagerAccessor().invoke(getWorldHandleAccessor().invoke(world));
} catch (Exception ignored) {
// method not available, fall through
}
}
throw new IllegalArgumentException();
}
if (MinecraftVersion.NETHER_UPDATE_2.atOrAbove()) {
if (dimensionImplConverter == null) {
dimensionImplConverter = new FauxEnumConverter<>(DimensionImpl.class, dimensionManager);
@@ -1335,9 +1469,31 @@ public class BukkitConverters {
@Override
public Integer getSpecific(Object generic) {
if (dimensionManager == null) {
dimensionManager = MinecraftReflection.getNullableNMS("DimensionManager");
dimensionManager = MinecraftReflection.getDimensionManager();
}
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
if (dimensionKey == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(dimensionManager, false);
dimensionKey = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract
.newBuilder()
.typeExact(MinecraftReflection.getMinecraftKeyClass())
.banModifier(Modifier.STATIC)
.build()));
}
MinecraftKey key = MinecraftKey.fromHandle(dimensionKey.get(generic));
switch (key.getKey()) {
case "overworld":
return Dimension.OVERWORLD.getId();
case "the_nether":
return Dimension.THE_NETHER.getId();
case "the_end":
return Dimension.THE_END.getId();
default:
throw new IllegalArgumentException("id not supported for extra dimensions");
}
}
if (MinecraftVersion.NETHER_UPDATE_2.atOrAbove()) {
if (dimensionImplConverter == null) {
dimensionImplConverter = new FauxEnumConverter<>(DimensionImpl.class, dimensionManager);
@@ -1393,7 +1549,7 @@ public class BukkitConverters {
@Override
public Object getGeneric(List<MerchantRecipe> specific) {
if (merchantRecipeListConstructor == null) {
Class<?> merchantRecipeListClass = MinecraftReflection.getMinecraftClass("MerchantRecipeList");
Class<?> merchantRecipeListClass = MinecraftReflection.getMerchantRecipeList();
merchantRecipeListConstructor = Accessors.getConstructorAccessor(merchantRecipeListClass);
Class<?> craftMerchantRecipeClass = MinecraftReflection.getCraftBukkitClass("inventory.CraftMerchantRecipe");
FuzzyReflection reflection = FuzzyReflection.fromClass(craftMerchantRecipeClass, false);
@@ -1407,7 +1563,9 @@ public class BukkitConverters {
@Override
public List<MerchantRecipe> getSpecific(Object generic) {
if (nmsMerchantRecipeToBukkit == null) {
Class<?> merchantRecipeClass = MinecraftReflection.getMinecraftClass("MerchantRecipe");
Class<?> merchantRecipeClass = MinecraftReflection.getMinecraftClass(
"world.item.trading.MerchantRecipe","MerchantRecipe"
);
FuzzyReflection reflection = FuzzyReflection.fromClass(merchantRecipeClass, false);
nmsMerchantRecipeToBukkit = Accessors.getMethodAccessor(reflection.getMethodByName("asBukkit"));
}
@@ -1432,7 +1590,7 @@ public class BukkitConverters {
@Override
public Object getGeneric(BlockPosition specific) {
if (sectionPositionClass == null) {
sectionPositionClass = MinecraftReflection.getMinecraftClass("SectionPosition");
sectionPositionClass = MinecraftReflection.getSectionPosition();
}
if (sectionPositionCreate == null) {
@@ -1461,4 +1619,73 @@ public class BukkitConverters {
}
});
}
private static Field gameStateMapField;
private static Field gameStateIdField;
public static EquivalentConverter<Integer> getGameStateConverter() {
return new EquivalentConverter<Integer>() {
@Override
public Object getGeneric(Integer specific) {
if (specific == null) {
specific = 0;
}
if (MinecraftVersion.NETHER_UPDATE.atOrAbove()) {
if (gameStateMapField == null) {
Class<?> stateClass = MinecraftReflection.getGameStateClass();
gameStateMapField = FuzzyReflection
.fromClass(stateClass, true)
.getField(FuzzyFieldContract
.newBuilder()
.typeDerivedOf(Map.class)
.build());
gameStateMapField.setAccessible(true);
}
try {
Map<Integer, Object> map = (Map<Integer, Object>) gameStateMapField.get(null);
return map.get(specific);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
} else {
return specific;
}
}
@Override
public Integer getSpecific(Object generic) {
if (generic == null) {
return 0;
}
if (MinecraftVersion.NETHER_UPDATE.atOrAbove()) {
if (gameStateIdField == null) {
Class<?> stateClass = MinecraftReflection.getGameStateClass();
gameStateIdField = FuzzyReflection
.fromClass(stateClass, true)
.getField(FuzzyFieldContract
.newBuilder()
.typeExact(int.class)
.build());
gameStateIdField.setAccessible(true);
}
try {
return (Integer) gameStateIdField.get(generic);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
} else {
return (Integer) generic;
}
}
@Override
public Class<Integer> getSpecificType() {
return Integer.class;
}
};
}
}
@@ -1,12 +1,18 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
/**
* Represents a ChunkCoordIntPair.
* @author Kristian
@@ -79,9 +85,14 @@ public class ChunkCoordIntPair {
public ChunkCoordIntPair getSpecific(Object generic) {
if (MinecraftReflection.isChunkCoordIntPair(generic)) {
if (COORD_X == null || COORD_Z == null) {
FieldAccessor[] ints = Accessors.getFieldAccessorArray(COORD_PAIR_CLASS, int.class, true);
COORD_X = ints[0];
COORD_Z = ints[1];
FuzzyReflection fuzzy = FuzzyReflection.fromClass(COORD_PAIR_CLASS, true);
List<Field> fields = fuzzy.getFieldList(FuzzyFieldContract
.newBuilder()
.banModifier(Modifier.STATIC)
.typeExact(int.class)
.build());
COORD_X = Accessors.getFieldAccessor(fields.get(0));
COORD_Z = Accessors.getFieldAccessor(fields.get(1));
}
return new ChunkCoordIntPair((Integer) COORD_X.get(generic), (Integer) COORD_Z.get(generic));
}
@@ -17,6 +17,7 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Array;
import java.util.Optional;
import java.util.function.Function;
import com.comphenix.protocol.reflect.EquivalentConverter;
@@ -150,4 +151,24 @@ public class Converters {
}
};
}
public static <T> EquivalentConverter<Optional<T>> optional(final EquivalentConverter<T> converter) {
return new EquivalentConverter<Optional<T>>() {
@Override
public Object getGeneric(Optional<T> specific) {
return specific.map(converter::getGeneric);
}
@Override
public Optional<T> getSpecific(Object generic) {
Optional<Object> optional = (Optional<Object>) generic;
return optional.map(converter::getSpecific);
}
@Override
public Class<Optional<T>> getSpecificType() {
return (Class<Optional<T>>) Optional.empty().getClass();
}
};
}
}
@@ -10,6 +10,7 @@ import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Maps;
@@ -383,6 +384,7 @@ public abstract class EnumWrappers {
SWIMMING,
SPIN_ATTACK,
CROUCHING,
LONG_JUMPING,
DYING;
private final static EquivalentConverter<EntityPose> POSE_CONVERTER = EnumWrappers.getEntityPoseConverter();
@@ -481,7 +483,6 @@ public abstract class EnumWrappers {
DIFFICULTY_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 1);
}
ENTITY_USE_ACTION_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 0);
GAMEMODE_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 0);
RESOURCE_PACK_STATUS_CLASS = getEnum(PacketType.Play.Client.RESOURCE_PACK_STATUS.getPacketClass(), 0);
PLAYER_INFO_ACTION_CLASS = getEnum(PacketType.Play.Server.PLAYER_INFO.getPacketClass(), 0);
@@ -496,21 +497,29 @@ public abstract class EnumWrappers {
try {
// TODO enum names are more stable than their packet associations
ITEM_SLOT_CLASS = MinecraftReflection.getMinecraftClass("EnumItemSlot");
ITEM_SLOT_CLASS = MinecraftReflection.getMinecraftClass("world.entity.EnumItemSlot", "EnumItemSlot");
} catch (Exception ex) {
ITEM_SLOT_CLASS = getEnum(PacketType.Play.Server.ENTITY_EQUIPMENT.getPacketClass(), 0);
}
HAND_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 1);
// In 1.17 the hand and use action class is no longer a field in the packet
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
HAND_CLASS = MinecraftReflection.getMinecraftClass("world.EnumHand");
// class is named 'b' in the packet but class order differs in spigot and paper so we can only use the first method's return type (safest way)
ENTITY_USE_ACTION_CLASS = MinecraftReflection.getEnumEntityUseActionClass().getMethods()[0].getReturnType();
} else {
HAND_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 1);
ENTITY_USE_ACTION_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 0);
}
DIRECTION_CLASS = getEnum(PacketType.Play.Server.SPAWN_ENTITY_PAINTING.getPacketClass(), 0);
CHAT_TYPE_CLASS = getEnum(PacketType.Play.Server.CHAT.getPacketClass(), 0);
ENTITY_POSE_CLASS = MinecraftReflection.getNullableNMS("EntityPose");
ENTITY_POSE_CLASS = MinecraftReflection.getNullableNMS("world.entity.EntityPose", "EntityPose");
associate(PROTOCOL_CLASS, Protocol.class, getClientCommandConverter());
associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
associate(CHAT_VISIBILITY_CLASS, ChatVisibility.class, getChatVisibilityConverter());
associate(DIFFICULTY_CLASS, Difficulty.class, getDifficultyConverter());
associate(ENTITY_USE_ACTION_CLASS, EntityUseAction.class, getEntityUseActionConverter());
associate(GAMEMODE_CLASS, NativeGameMode.class, getGameModeConverter());
associate(RESOURCE_PACK_STATUS_CLASS, ResourcePackStatus.class, getResourcePackStatusConverter());
associate(PLAYER_INFO_ACTION_CLASS, PlayerInfoAction.class, getPlayerInfoActionConverter());
@@ -523,15 +532,14 @@ public abstract class EnumWrappers {
associate(PARTICLE_CLASS, Particle.class, getParticleConverter());
associate(SOUND_CATEGORY_CLASS, SoundCategory.class, getSoundCategoryConverter());
associate(ITEM_SLOT_CLASS, ItemSlot.class, getItemSlotConverter());
associate(HAND_CLASS, Hand.class, getHandConverter());
associate(DIRECTION_CLASS, Direction.class, getDirectionConverter());
associate(CHAT_TYPE_CLASS, ChatType.class, getChatTypeConverter());
associate(HAND_CLASS, Hand.class, getHandConverter());
associate(ENTITY_USE_ACTION_CLASS, EntityUseAction.class, getEntityUseActionConverter());
if (ENTITY_POSE_CLASS != null) {
associate(ENTITY_POSE_CLASS, EntityPose.class, getEntityPoseConverter());
}
INITIALIZED = true;
}
private static void associate(Class<?> nativeClass, Class<?> wrapperClass, EquivalentConverter<?> converter) {
@@ -550,12 +558,19 @@ public abstract class EnumWrappers {
* @return The type of the enum field.
*/
private static Class<?> getEnum(Class<?> clazz, int index) {
try {
return FuzzyReflection.fromClass(clazz, true).getFieldListByType(Enum.class).get(index).getType();
} catch (Throwable ex) {
ProtocolLogger.debug("Exception getting enum from " + clazz + " at index " + index, ex);
if (clazz == null) {
// not supported in the current version
return null;
}
List<Field> enumFields = FuzzyReflection.fromClass(clazz, true).getFieldListByType(Enum.class);
if (enumFields.size() <= index) {
// also probably not supported
ProtocolLogger.debug("Enum field not found at index {0} of {1}", index, clazz);
return null;
}
return enumFields.get(index).getType();
}
public static Map<Class<?>, EquivalentConverter<?>> getFromNativeMap() {
@@ -772,22 +787,17 @@ public abstract class EnumWrappers {
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
*/
public static class EnumConverter<T extends Enum<T>> implements EquivalentConverter<T> {
private Class<?> genericType;
private Class<T> specificType;
private final Class<?> genericType;
private final Class<T> specificType;
public EnumConverter(Class<?> genericType, Class<T> specificType) {
Validate.notNull(specificType, "specificType cannot be null");
// would love to check if genericType is null, but it breaks other stuff
this.genericType = genericType;
this.specificType = specificType;
}
@@ -806,10 +816,6 @@ public abstract class EnumWrappers {
public Class<T> getSpecificType() {
return specificType;
}
void setGenericType(Class<?> genericType) {
this.genericType = genericType;
}
}
public interface AliasedEnum {
@@ -10,7 +10,8 @@ import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import org.bukkit.util.Vector;
public class MovingObjectPositionBlock implements Cloneable {
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS("MovingObjectPositionBlock");
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS(
"world.phys.MovingObjectPositionBlock", "MovingObjectPositionBlock");
private BlockPosition position;
private Vector posVector;
@@ -17,12 +17,16 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.google.common.base.Objects;
/**
* Represents an immutable PlayerInfoData in the PLAYER_INFO packet.
@@ -94,14 +98,17 @@ public class PlayerInfoData {
public Object getGeneric(PlayerInfoData specific) {
if (constructor == null) {
try {
// public PlayerInfoData(Packet, GameProfile, int, GameMode, ChatComponent)
constructor = MinecraftReflection.getPlayerInfoDataClass().getConstructor(
MinecraftReflection.getMinecraftClass("PacketPlayOutPlayerInfo"),
MinecraftReflection.getGameProfileClass(),
int.class,
EnumWrappers.getGameModeClass(),
MinecraftReflection.getIChatBaseComponentClass()
);
List<Class<?>> args = new ArrayList<>();
if (!MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
args.add(PacketType.Play.Server.PLAYER_INFO.getPacketClass());
}
args.add(MinecraftReflection.getGameProfileClass());
args.add(int.class);
args.add(EnumWrappers.getGameModeClass());
args.add(MinecraftReflection.getIChatBaseComponentClass());
constructor = MinecraftReflection.getPlayerInfoDataClass().getConstructor(args.toArray(new Class<?>[0]));
} catch (Exception e) {
throw new RuntimeException("Cannot find PlayerInfoData constructor.", e);
}
@@ -110,16 +117,14 @@ public class PlayerInfoData {
// Attempt to construct the underlying PlayerInfoData
try {
// public PlayerInfoData(null, GameProfile, ping, GameMode, ChatComponent)
// The packet isn't a field, so it can be null
Object result = constructor.newInstance(
null,
specific.profile.handle,
specific.latency,
EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode),
specific.displayName != null ? specific.displayName.handle : null
);
return result;
Object gameMode = EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode);
Object displayName = specific.displayName != null ? specific.displayName.handle : null;
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
return constructor.newInstance(specific.profile.handle, specific.latency, gameMode, displayName);
} else {
return constructor.newInstance(null, specific.profile.handle, specific.latency, gameMode, displayName);
}
} catch (Exception e) {
throw new RuntimeException("Failed to construct PlayerInfoData.", e);
}
@@ -171,19 +176,19 @@ public class PlayerInfoData {
if (obj instanceof PlayerInfoData) {
PlayerInfoData other = (PlayerInfoData) obj;
return profile.equals(other.profile) && latency == other.latency && gameMode == other.gameMode
&& displayName.equals(other.displayName);
&& Objects.equals(displayName, other.displayName);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(latency, gameMode, profile, displayName);
return Objects.hash(latency, gameMode, profile, displayName);
}
@Override
public String toString() {
return String.format("PlayerInfoData[latency=%s, gameMode=%s, profile=%s, displayName=%s",
return String.format("PlayerInfoData[latency=%s, gameMode=%s, profile=%s, displayName=%s]",
latency, gameMode, profile, displayName);
}
}
@@ -83,10 +83,10 @@ public class Vector3F {
}
private static Constructor<?> constructor = null;
private static Class<?> clazz = MinecraftReflection.getMinecraftClass("Vector3f");
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS("core.Vector3f","Vector3f");
public static Class<?> getMinecraftClass() {
return clazz;
return NMS_CLASS;
}
public static EquivalentConverter<Vector3F> getConverter() {
@@ -100,7 +100,7 @@ public class Vector3F {
public Object getGeneric(Vector3F specific) {
if (constructor == null) {
try {
constructor = clazz.getConstructor(float.class, float.class, float.class);
constructor = NMS_CLASS.getConstructor(float.class, float.class, float.class);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to find constructor for Vector3f", ex);
}
@@ -3,13 +3,12 @@ package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import java.util.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
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 com.comphenix.protocol.utility.MinecraftVersion;
@@ -34,9 +33,6 @@ public class WrappedAttribute extends AbstractWrapper {
// The one constructor
private static Constructor<?> ATTRIBUTE_CONSTRUCTOR;
private static Object REGISTRY = null;
private static MethodAccessor REGISTRY_GET = null;
private static final Map<String, String> REMAP;
static {
@@ -76,7 +72,7 @@ public class WrappedAttribute extends AbstractWrapper {
// Initialize modifier
if (ATTRIBUTE_MODIFIER == null) {
ATTRIBUTE_MODIFIER = new StructureModifier<Object>(MinecraftReflection.getAttributeSnapshotClass());
ATTRIBUTE_MODIFIER = new StructureModifier<>(MinecraftReflection.getAttributeSnapshotClass());
}
this.modifier = ATTRIBUTE_MODIFIER.withTarget(handle);
}
@@ -115,7 +111,9 @@ public class WrappedAttribute extends AbstractWrapper {
public String key;
}
private static final Class<?> ATTRIBUTE_BASE_CLASS = MinecraftReflection.getNullableNMS("AttributeBase");
private static final Class<?> ATTRIBUTE_BASE_CLASS = MinecraftReflection.getNullableNMS(
"world.entity.ai.attributes.AttributeBase", "AttributeBase"
);
private static final AutoWrapper<WrappedAttributeBase> ATTRIBUTE_BASE = AutoWrapper.wrap(
WrappedAttributeBase.class, ATTRIBUTE_BASE_CLASS
@@ -167,8 +165,14 @@ public class WrappedAttribute extends AbstractWrapper {
/**
* Retrieve the parent update attributes packet.
* @return The parent packet.
* @deprecated Removed in 1.17
*/
@Nullable
public PacketContainer getParentPacket() {
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
return null;
}
return new PacketContainer(
PacketType.Play.Server.UPDATE_ATTRIBUTES,
modifier.withType(MinecraftReflection.getPacketClass()).read(0)
@@ -223,7 +227,7 @@ public class WrappedAttribute extends AbstractWrapper {
}
};
attributeModifiers = new CachedSet<WrappedAttributeModifier>(converted);
attributeModifiers = new CachedSet<>(converted);
}
return Collections.unmodifiableSet(attributeModifiers);
}
@@ -246,9 +250,7 @@ public class WrappedAttribute extends AbstractWrapper {
if (getBaseValue() == other.getBaseValue() &&
Objects.equal(getAttributeKey(), other.getAttributeKey())) {
return getModifiers().stream()
.filter((elem) -> !other.getModifiers().contains(elem))
.count() == 0;
return other.getModifiers().containsAll(getModifiers());
}
}
return false;
@@ -411,45 +413,33 @@ public class WrappedAttribute extends AbstractWrapper {
* @throws RuntimeException If anything went wrong with the reflection.
*/
public WrappedAttribute build() {
Preconditions.checkNotNull(packet, "packet cannot be NULL.");
Preconditions.checkNotNull(attributeKey, "attributeKey cannot be NULL.");
// Remember to set the base value
if (Double.isNaN(baseValue)) {
throw new IllegalStateException("Base value has not been set.");
}
// Retrieve the correct constructor
boolean isStatic = MinecraftVersion.CAVES_CLIFFS_1.atOrAbove();
if (ATTRIBUTE_CONSTRUCTOR == null) {
ATTRIBUTE_CONSTRUCTOR = FuzzyReflection.fromClass(MinecraftReflection.getAttributeSnapshotClass(), true)
.getConstructor(FuzzyMethodContract.newBuilder().parameterCount(4)
.parameterDerivedOf(MinecraftReflection.getPacketClass(), 0)
.parameterExactType(double.class, 2).parameterDerivedOf(Collection.class, 3)
.build()
);
FuzzyReflection ref = FuzzyReflection.fromClass(MinecraftReflection.getAttributeSnapshotClass(), true);
FuzzyMethodContract.Builder contract = FuzzyMethodContract.newBuilder().parameterCount(isStatic ? 3 : 4);
if (!isStatic) {
contract.parameterDerivedOf(MinecraftReflection.getPacketClass(), 0);
}
contract.parameterExactType(double.class).parameterDerivedOf(Collection.class);
ATTRIBUTE_CONSTRUCTOR = ref.getConstructor(contract.build());
// Just in case
ATTRIBUTE_CONSTRUCTOR.setAccessible(true);
}
Object attributeKey;
if (KEY_WRAPPED) {
if (REGISTRY == null) {
Class<?> iRegistry = MinecraftReflection.getMinecraftClass("IRegistry");
try {
REGISTRY = iRegistry.getDeclaredField("ATTRIBUTE").get(null);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to obtain ATTRIBUTE registry", ex);
}
}
if (REGISTRY_GET == null) {
Class<?> keyClass = MinecraftReflection.getMinecraftKeyClass();
REGISTRY_GET = Accessors.getMethodAccessor(REGISTRY.getClass(), "get", keyClass);
}
WrappedRegistry registry = WrappedRegistry.getAttributeRegistry();
String strKey = REMAP.getOrDefault(this.attributeKey, this.attributeKey);
Object key = MinecraftKey.getConverter().getGeneric(new MinecraftKey(strKey));
attributeKey = REGISTRY_GET.invoke(REGISTRY, key);
attributeKey = registry.get(strKey);
if (attributeKey == null) {
throw new IllegalArgumentException("Invalid attribute name: " + this.attributeKey);
@@ -459,15 +449,14 @@ public class WrappedAttribute extends AbstractWrapper {
}
try {
Object handle = ATTRIBUTE_CONSTRUCTOR.newInstance(
packet.getHandle(),
attributeKey,
baseValue,
getUnwrappedModifiers());
Object handle;
if (isStatic) {
handle = ATTRIBUTE_CONSTRUCTOR.newInstance(attributeKey, baseValue, getUnwrappedModifiers());
} else {
handle = ATTRIBUTE_CONSTRUCTOR.newInstance(packet.getHandle(), attributeKey, baseValue, getUnwrappedModifiers());
}
// Create it
return new WrappedAttribute(handle);
} catch (Exception e) {
throw new RuntimeException("Cannot construct AttributeSnapshot.", e);
}
@@ -66,7 +66,9 @@ public class WrappedAttributeModifier extends AbstractWrapper {
}
static {
OPERATION_CLASS = OPERATION_ENUM ? MinecraftReflection.getMinecraftClass("AttributeModifier$Operation") : null;
OPERATION_CLASS = OPERATION_ENUM ? MinecraftReflection.getMinecraftClass(
"world.entity.ai.attributes.AttributeModifier$Operation", "AttributeModifier$Operation"
) : null;
OPERATION_CONVERTER = OPERATION_ENUM ? new IndexedEnumConverter<>(Operation.class, OPERATION_CLASS) : null;
}
@@ -42,7 +42,7 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
}
try {
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getMinecraftClass("ChatDeserializer"), true)
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getChatDeserializer(), true)
.getMethodByParameters("deserialize", Object.class, new Class<?>[] { GSON_CLASS, String.class, Class.class, boolean.class }));
} catch (IllegalArgumentException ex) {
// We'll handle it in the ComponentParser
@@ -50,7 +50,7 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
}
// Get a component from a standard Minecraft message
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(MinecraftReflection.getCraftChatMessage(), "fromString", String.class);
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(MinecraftReflection.getCraftChatMessage(), "fromString", String.class, boolean.class);
// And the component text constructor
CONSTRUCT_TEXT_COMPONENT = Accessors.getConstructorAccessor(MinecraftReflection.getChatComponentTextClass(), String.class);
@@ -95,6 +95,9 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
/**
* Construct a wrapper around a new text chat component with the given text.
* <p>
* Note: {@link #fromLegacyText(String)} is preferred for text that contains
* legacy formatting codes since it will translate them to the JSON equivalent.
* @param text - the text of the text chat component.
* @return The wrapper around the new chat component.
*/
@@ -111,7 +114,7 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
* @return The equivalent chat components.
*/
public static WrappedChatComponent[] fromChatMessage(String message) {
Object[] components = (Object[]) CONSTRUCT_COMPONENT.invoke(null, message);
Object[] components = (Object[]) CONSTRUCT_COMPONENT.invoke(null, message, false);
WrappedChatComponent[] result = new WrappedChatComponent[components.length];
for (int i = 0; i < components.length; i++) {
@@ -120,6 +123,18 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
return result;
}
/**
* Construct a single chat component from a standard Minecraft message
* (with legacy formatting codes), preserving multiple lines.
* @param message - the message.
* @return The equivalent chat component.
*/
public static WrappedChatComponent fromLegacyText(String message) {
// With keepNewlines = true (second parameter), only one component is returned
Object[] components = (Object[]) CONSTRUCT_COMPONENT.invoke(null, message, true);
return fromHandle(components[0]);
}
/**
* Retrieve a copy of this component as a JSON string.
* <p>
@@ -129,8 +129,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
// 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);
eggConstructor = Accessors.getConstructorAccessor(
MinecraftReflection.getMinecraftClass("world.entity.projectile.EntityEgg", "EntityEgg"),
MinecraftReflection.getNmsWorldClass(), double.class, double.class, double.class
);
}
Object world = BukkitUnwrapper.getInstance().unwrapItem(Bukkit.getWorlds().get(0));
@@ -493,7 +495,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
.build();
List<Method> methods = fuzzy.getMethodList(contract);
for (Method method : methods) {
if (method.getName().equals("set") || method.getName().equals("watch")) {
if (method.getName().equals("set") || method.getName().equals("watch") || method.getName().equals("b")) {
SETTER = Accessors.getMethodAccessor(method);
} else {
REGISTER = Accessors.getMethodAccessor(method);
@@ -0,0 +1,200 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
import org.bukkit.util.Vector;
/**
* Represents an entity used action used in the UseEntity packet sent by the client.
* @author derklaro
*/
public class WrappedEnumEntityUseAction extends AbstractWrapper implements ClonableWrapper {
public static final EquivalentConverter<WrappedEnumEntityUseAction> CONVERTER = Converters.handle(AbstractWrapper::getHandle,
WrappedEnumEntityUseAction::fromHandle, WrappedEnumEntityUseAction.class);
private static final Class<?> PACKET_CLASS = PacketType.Play.Client.USE_ENTITY.getPacketClass();
private static final Class<?>[] DECLARED_CLASSES = PACKET_CLASS.getDeclaredClasses();
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getEnumEntityUseActionClass();
private static final MethodAccessor ACTION_USE = MinecraftReflection.getEntityUseActionEnumMethodAccessor();
private static final ConstructorAccessor INTERACT = useAction(EnumWrappers.getHandClass());
private static final ConstructorAccessor INTERACT_AT = useAction(EnumWrappers.getHandClass(),
MinecraftReflection.getVec3DClass());
private static final Object ATTACK = Accessors.getFieldAccessor(FuzzyReflection.fromClass(PACKET_CLASS, true)
.getField(FuzzyFieldContract.newBuilder()
.requireModifier(Modifier.STATIC)
.typeExact(MinecraftReflection.getEnumEntityUseActionClass())
.build())
).get(null);
private static final WrappedEnumEntityUseAction ATTACK_WRAPPER = new WrappedEnumEntityUseAction(ATTACK);
private final EntityUseAction action;
// these fields are only available for interact & interact_at
private FieldAccessor handAccessor;
private FieldAccessor positionAccessor;
/**
* Construct a new wrapper for the entity use action class in the UseEntity packet.
* @param handle - the NMS handle.
*/
private WrappedEnumEntityUseAction(Object handle) {
super(HANDLE_TYPE);
setHandle(handle);
action = EnumWrappers.getEntityUseActionConverter().getSpecific(ACTION_USE.invoke(handle));
}
/**
* Finds a constructor of a declared class in the UseEntity class. Used to find the action class implementations.
* @param parameterTypes - the types the constructor of the class must have.
* @return a constructor for a matching class.
* @throws IllegalArgumentException if no constructor was found.
*/
private static ConstructorAccessor useAction(Class<?>... parameterTypes) {
for (Class<?> subClass : DECLARED_CLASSES) {
ConstructorAccessor accessor = Accessors.getConstructorAccessorOrNull(subClass, parameterTypes);
if (accessor != null) {
return accessor;
}
}
throw new IllegalArgumentException(
"No constructor with " + Arrays.toString(parameterTypes) + " in " + PACKET_CLASS);
}
/**
* Construct a new wrapper for the entity use action class in the UseEntity packet.
* @param handle - the NMS handle.
* @return the created wrapper.
*/
public static WrappedEnumEntityUseAction fromHandle(Object handle) {
return new WrappedEnumEntityUseAction(handle);
}
/**
* Get the jvm static action for attacking an entity.
* @return the action for an entity attack.
*/
public static WrappedEnumEntityUseAction attack() {
return ATTACK_WRAPPER;
}
/**
* Get an action for interacting with an entity.
* @param hand - the hand used for the interact.
* @return the action for an interact.
*/
public static WrappedEnumEntityUseAction interact(Hand hand) {
Object handle = INTERACT.invoke(EnumWrappers.getHandConverter().getGeneric(hand));
return new WrappedEnumEntityUseAction(handle);
}
/**
* Get an action for interacting with an entity at a specific location.
* @param hand - the hand used for the interact.
* @param vector - the position of the interact.
* @return the action for an interact_at.
*/
public static WrappedEnumEntityUseAction interactAt(Hand hand, Vector vector) {
Object handle = INTERACT_AT.invoke(EnumWrappers.getHandConverter().getGeneric(hand),
BukkitConverters.getVectorConverter().getGeneric(vector));
return new WrappedEnumEntityUseAction(handle);
}
/**
* Get the action used for the interact.
* @return the interact action.
*/
public EntityUseAction getAction() {
return action;
}
/**
* Get the hand used for the interact. Only available if this represents interact or interact_at.
* @return the hand used for the interact.
* @throws IllegalArgumentException if called for attack.
*/
public Hand getHand() {
return EnumWrappers.getHandConverter().getSpecific(getHandAccessor().get(handle));
}
/**
* Sets the hand used for the interact.
* @param hand the used hand.
* @throws IllegalArgumentException if called for attack.
*/
public void setHand(Hand hand) {
getHandAccessor().set(handle, EnumWrappers.getHandConverter().getGeneric(hand));
}
/**
* Get the position of the interact. Only available if this represents interact_at.
* @return the position of the interact.
* @throws IllegalArgumentException if called for attack or interact.
*/
public Vector getPosition() {
return BukkitConverters.getVectorConverter().getSpecific(getPositionAccessor().get(handle));
}
/**
* Sets the position of the interact.
* @param position the position.
* @throws IllegalArgumentException if called for attack or interact.
*/
public void setPosition(Vector position) {
getPositionAccessor().set(handle, BukkitConverters.getVectorConverter().getGeneric(position));
}
@Override
public WrappedEnumEntityUseAction deepClone() {
switch (action) {
case ATTACK:
return WrappedEnumEntityUseAction.attack();
case INTERACT:
return WrappedEnumEntityUseAction.interact(getHand());
case INTERACT_AT:
return WrappedEnumEntityUseAction.interactAt(getHand(), getPosition());
default:
throw new IllegalArgumentException("Invalid EntityUseAction: " + action);
}
}
/**
* Get a field accessor for the hand in the interact and interact_at type.
* @return a field accessor for the hand field.
* @throws IllegalArgumentException if called for attack.
*/
private FieldAccessor getHandAccessor() {
if (handAccessor == null) {
handAccessor = MinecraftReflection.getHandEntityUseActionEnumFieldAccessor(handle);
}
return handAccessor;
}
/**
* Get a field accessor for the position in the interact_at type.
* @return a field accessor for the position field.
* @throws IllegalArgumentException if called for attack or interact.
*/
public FieldAccessor getPositionAccessor() {
if (positionAccessor == null) {
positionAccessor = MinecraftReflection.getVec3EntityUseActionEnumFieldAccessor(handle);
}
return positionAccessor;
}
}
@@ -9,6 +9,8 @@ import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.mojang.math.Vector3fa;
import org.bukkit.Color;
import org.bukkit.Particle;
import org.bukkit.inventory.ItemStack;
@@ -31,14 +33,14 @@ public class WrappedParticle<T> {
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(Particle.class)
.parameterExactType(MinecraftReflection.getMinecraftClass("ParticleParam"))
.parameterExactType(MinecraftReflection.getParticleParam())
.build();
toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(MinecraftReflection.getMinecraftClass("ParticleParam"))
.returnTypeExact(MinecraftReflection.getParticleParam())
.parameterCount(2)
.build();
toNMS = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
@@ -130,12 +132,26 @@ public class WrappedParticle<T> {
}
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));
int r, g, b;
float alpha;
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
Vector3fa rgb = (Vector3fa) modifier.withType(Vector3fa.class).read(0);
r = (int) (rgb.a() * 255);
g = (int) (rgb.b() * 255);
b = (int) (rgb.c() * 255);
alpha = (float) modifier.withType(float.class).read(0);
} else {
StructureModifier<Float> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class);
r = (int) (modifier.read(0) * 255);
g = (int) (modifier.read(1) * 255);
b = (int) (modifier.read(2) * 255);
alpha = modifier.read(3);
}
return new Particle.DustOptions(Color.fromRGB(r, g, b), alpha);
}
public static <T> WrappedParticle<T> create(Particle particle, T data) {
@@ -0,0 +1,91 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.ImmutableMap;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
public class WrappedRegistry {
// map of NMS class to registry instance
private static final Map<Class<?>, WrappedRegistry> REGISTRY;
private static final MethodAccessor GET;
private static final MethodAccessor GET_KEY;
static {
Map<Class<?>, WrappedRegistry> regMap = new HashMap<>();
Class<?> regClass = MinecraftReflection.getIRegistry();
if (regClass != null) {
for (Field field : regClass.getFields()) {
try {
// make sure it's actually a registry
if (field.getType().isAssignableFrom(regClass)) {
Type genType = field.getGenericType();
if (genType instanceof ParameterizedType) {
ParameterizedType par = (ParameterizedType) genType;
Type paramType = par.getActualTypeArguments()[0];
if (paramType instanceof Class) {
regMap.put((Class<?>) paramType, new WrappedRegistry(field.get(null)));
}
}
}
} catch (ReflectiveOperationException ignored) {
}
}
}
REGISTRY = ImmutableMap.copyOf(regMap);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(regClass, false);
GET = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnDerivedOf(Object.class)
.requireModifier(Modifier.ABSTRACT)
.parameterExactType(MinecraftReflection.getMinecraftKeyClass())
.build()));
GET_KEY = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeExact(MinecraftReflection.getMinecraftKeyClass())
.build()));
}
private final Object handle;
private WrappedRegistry(Object handle) {
this.handle = handle;
}
public Object get(MinecraftKey key) {
return GET.invoke(handle, MinecraftKey.getConverter().getGeneric(key));
}
public Object get(String key) {
return get(new MinecraftKey(key));
}
public MinecraftKey getKey(Object generic) {
return MinecraftKey.getConverter().getSpecific(GET_KEY.invoke(handle, generic));
}
// TODO add more methods
public static WrappedRegistry getAttributeRegistry() {
return REGISTRY.get(MinecraftReflection.getAttributeBase());
}
public static WrappedRegistry getDimensionRegistry() {
return REGISTRY.get(MinecraftReflection.getDimensionManager());
}
}
@@ -161,7 +161,7 @@ public class WrappedServerPing extends AbstractWrapper implements ClonableWrappe
* @param message - the message.
*/
public void setMotD(String message) {
setMotD(WrappedChatComponent.fromText(message));
setMotD(WrappedChatComponent.fromLegacyText(message));
}
/**
@@ -7,9 +7,12 @@ import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
public class WrappedVillagerData extends AbstractWrapper implements ClonableWrapper {
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS("VillagerData");
private static final Class<?> TYPE_CLASS = MinecraftReflection.getNullableNMS("VillagerType");
private static final Class<?> PROF_CLASS = MinecraftReflection.getNullableNMS("VillagerProfession");
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS(
"world.entity.npc.VillagerData","VillagerData");
private static final Class<?> TYPE_CLASS = MinecraftReflection.getNullableNMS(
"world.entity.npc.VillagerType", "VillagerType");
private static final Class<?> PROF_CLASS = MinecraftReflection.getNullableNMS(
"world.entity.npc.VillagerProfession", "VillagerProfession");
private static EquivalentConverter<Type> TYPE_CONVERTER;
private static EquivalentConverter<Profession> PROF_CONVERTER;
@@ -592,7 +592,7 @@ public class NbtFactory {
Constructor<?> constructor = CONSTRUCTORS.get(type);
if (constructor == null) {
if (getTagType == null) {
Class<?> tagTypes = MinecraftReflection.getMinecraftClass("NBTTagTypes");
Class<?> tagTypes = MinecraftReflection.getNbtTagTypes();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(tagTypes, false);
getTagType = fuzzy.getMethod(
FuzzyMethodContract.newBuilder().parameterCount(1).parameterExactType(int.class).build());
@@ -35,7 +35,8 @@ import org.bukkit.block.BlockState;
* @author Kristian
*/
class TileEntityAccessor<T extends BlockState> {
private static final boolean BLOCK_DATA_INCL = MinecraftVersion.NETHER_UPDATE.atOrAbove();
private static final boolean BLOCK_DATA_INCL = MinecraftVersion.NETHER_UPDATE.atOrAbove()
&& !MinecraftVersion.CAVES_CLIFFS_1.atOrAbove();
/**
* Token indicating that the given block state doesn't contain any tile entities.
@@ -3,6 +3,10 @@ package com.comphenix.integration.protocol;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.reflect.FieldUtils;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
@@ -10,7 +14,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.simple.SimpleLogger;
import org.bukkit.Bukkit;
@@ -18,41 +21,25 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoadOrder;
import org.bukkit.plugin.PluginManager;
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 org.powermock.core.classloader.annotations.PrepareForTest;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.reflect.FieldUtils;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
// TODO Migrate this to Gradle if necessary
// Damn final classes ...
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
@PrepareForTest(PluginDescriptionFile.class)
public class SimpleCraftBukkitITCase {
// The fake plugin
private static volatile Plugin FAKE_PLUGIN = null;
/**
* The maximum amount of time to wait before a server has started.
* <p>
* We have to give it the ample time of 60 seconds, as a server may have to
* generate the spawn area in three worlds.
* We have to give it the ample time of 60 seconds, as a server may have to generate the spawn area in three worlds.
*/
private static final int TIMEOUT_MS = 60000;
// The fake plugin
private static volatile Plugin FAKE_PLUGIN = null;
/**
* Setup the CraftBukkit server for all the tests.
* @throws IOException Unable to setup server.
*
* @throws IOException Unable to setup server.
* @throws InterruptedException Thread interrupted.
*/
//@BeforeClass
//@BeforeAll
public static void setupCraftBukkit() throws Exception {
setupPlugins();
@@ -63,17 +50,18 @@ public class SimpleCraftBukkitITCase {
System.out.println("Loader of SimpleLogger: " + SimpleLogger.class.getClassLoader());
System.out.println("Loader of Logger: " + Logger.class.getClassLoader());
}
// We need to wait until the server object is ready
while (Bukkit.getServer() == null)
while (Bukkit.getServer() == null) {
Thread.sleep(1);
}
// Make it clear this plugin doesn't exist
FAKE_PLUGIN = createPlugin("FakeTestPluginIntegration");
// No need to look for updates
FieldUtils.writeStaticField(ProtocolLibrary.class, "UPDATES_DISABLED", Boolean.TRUE, true);
// Wait until the server and all the plugins have loaded
Bukkit.getScheduler().callSyncMethod(FAKE_PLUGIN, new Callable<Object>() {
@Override
@@ -82,26 +70,22 @@ public class SimpleCraftBukkitITCase {
return null;
}
}).get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
// Plugins are now ready
ProtocolLibrary.getConfig().setDebug(true);
}
/**
* Close the CraftBukkit server when they're done.
*/
//@AfterClass
//@AfterAll
public static void shutdownCraftBukkit() {
Bukkit.shutdown();
}
//@Test
public void testPingPacket() throws Throwable {
TestPingPacket.newTest().startTest(FAKE_PLUGIN);
}
/**
* Copy ProtocolLib into the plugins folder.
*
* @throws IOException If anything went wrong.
*/
private static void setupPlugins() throws IOException {
@@ -109,27 +93,27 @@ public class SimpleCraftBukkitITCase {
File srcDirectory = new File("../");
File bestFile = null;
int bestLength = Integer.MAX_VALUE;
for (File file : srcDirectory.listFiles()) {
String name = file.getName();
if (file.isFile() && name.startsWith("ProtocolLib") && name.length() < bestLength) {
bestLength = name.length();
bestFile = file;
}
}
if (bestFile == null) {
throw new IllegalStateException("Cannot find ProtocolLib in " + srcDirectory);
}
// Copy the ProtocolLib plugin to the server
if (pluginDirectory.exists()) {
deleteFolder(pluginDirectory);
}
pluginDirectory.mkdirs();
File destination = new File(pluginDirectory, bestFile.getName()).getAbsoluteFile();
Files.copy(bestFile, destination);
}
@@ -148,46 +132,53 @@ public class SimpleCraftBukkitITCase {
}
}
}
/**
* Load a specific fake plugin.
*
* @param plugin - the plugin to load.
*/
@SuppressWarnings("unchecked")
private static void initializePlugin(Plugin plugin) {
PluginManager manager = Bukkit.getPluginManager();
try {
List<Plugin> plugins = (List<Plugin>) FieldUtils.readField(manager, "plugins", true);
Map<String, Plugin> lookupNames = (Map<String, Plugin>) FieldUtils.readField(manager, "lookupNames", true);
/// Associate this plugin
plugins.add(plugin);
lookupNames.put(plugin.getName(), plugin);
Map<String, Plugin> lookupNames = (Map<String, Plugin>) FieldUtils.readField(manager, "lookupNames", true);
/// Associate this plugin
plugins.add(plugin);
lookupNames.put(plugin.getName(), plugin);
} catch (Exception e) {
throw new RuntimeException("Unable to access the fields of " + manager, e);
}
}
/**
* Create a mockable plugin for all the tests.
*
* @param fakePluginName - the fake plugin name.
* @return The plugin.
*/
private static Plugin createPlugin(String fakePluginName) {
Plugin plugin = mock(Plugin.class);
PluginDescriptionFile description = mock(PluginDescriptionFile.class);
when(description.getDepend()).thenReturn(Lists.newArrayList("ProtocolLib"));
when(description.getSoftDepend()).thenReturn(Collections.<String>emptyList());
when(description.getLoadBefore()).thenReturn(Collections.<String>emptyList());
when(description.getSoftDepend()).thenReturn(Collections.emptyList());
when(description.getLoadBefore()).thenReturn(Collections.emptyList());
when(description.getLoad()).thenReturn(PluginLoadOrder.POSTWORLD);
when(plugin.getName()).thenReturn(fakePluginName);
when(plugin.getServer()).thenReturn(Bukkit.getServer());
when(plugin.isEnabled()).thenReturn(true);
when(plugin.getDescription()).thenReturn(description);
return plugin;
}
//@Test
public void testPingPacket() throws Throwable {
TestPingPacket.newTest().startTest(FAKE_PLUGIN);
}
}
@@ -1,5 +1,10 @@
package com.comphenix.integration.protocol;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.utility.StreamSerializer;
import com.google.common.io.ByteStreams;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@@ -10,84 +15,110 @@ import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.utility.StreamSerializer;
import com.google.common.io.ByteStreams;
public class SimpleMinecraftClient {
private static final int CONNECT_TIMEOUT = 2500;
private static final int READ_TIMEOUT = 15000;
// Current Minecraft version
private final int protocolVersion;
private static final int CONNECT_TIMEOUT = 2500;
private static final int READ_TIMEOUT = 15000;
// Typical Minecraft serializer
private static final StreamSerializer serializer = StreamSerializer.getDefault();
// Current Minecraft version
private final int protocolVersion;
// Typical Minecraft serializer
private static StreamSerializer serializer = StreamSerializer.getDefault();
public SimpleMinecraftClient(int protocolVersion) {
public SimpleMinecraftClient(int protocolVersion) {
this.protocolVersion = protocolVersion;
}
/**
* Query the local server for ping information.
* @return The server information.
* @throws IOException
*/
public String queryLocalPing() throws IOException {
return queryServerPing(new InetSocketAddress("localhost", 25565));
/**
* Write a byte array to the output stream, prefixed by a length.
*
* @param output - the stream.
* @param data - the data to write.
*/
private static void writeByteArray(DataOutputStream output, byte[] data) throws IOException {
StreamSerializer.getDefault().serializeVarInt(output, data.length);
if (data.length > 0) {
output.write(data);
}
}
/**
* Read a byte array from an input stream, prefixed by length.
*
* @param input - the input stream.
* @return The read byte array.
*/
private static byte[] readByteArray(DataInputStream input) throws IOException {
int length = serializer.deserializeVarInt(input);
byte[] data = new byte[length];
ByteStreams.readFully(input, data);
return data;
}
/**
* Query the local server for ping information.
*
* @return The server information.
* @throws IOException
*/
public String queryLocalPing() throws IOException {
return this.queryServerPing(new InetSocketAddress("localhost", 25565));
}
/**
* Query the given server for its list ping information.
*
* @param address - the server hostname and port.
* @return The server information.
* @throws IOException
*/
public String queryServerPing(InetSocketAddress address) throws IOException {
Socket socket = null;
OutputStream output = null;
InputStream input = null;
try {
socket = new Socket();
socket.connect(address, CONNECT_TIMEOUT);
Socket socket = null;
OutputStream output = null;
InputStream input = null;
// Shouldn't take that long
socket.setSoTimeout(READ_TIMEOUT);
// Retrieve sockets
output = socket.getOutputStream();
input = socket.getInputStream();
try {
socket = new Socket();
socket.connect(address, CONNECT_TIMEOUT);
// The output writer
DataOutputStream data = new DataOutputStream(output);
// Shouldn't take that long
socket.setSoTimeout(READ_TIMEOUT);
// Request a server information packet
writePacket(data, new HandshakePacket(protocolVersion, address.getHostName(), address.getPort(), 1));
writePacket(data, new RequestPacket());
data.flush();
// Read a single packet, and close the connection
SimplePacket packet = readPacket(new DataInputStream(input), Protocol.STATUS);
socket.close();
return ((ResponsePacket) packet).getPingJson();
} finally {
if (input != null)
input.close();
if (output != null)
output.close();
if (socket != null)
socket.close();
}
// Retrieve sockets
output = socket.getOutputStream();
input = socket.getInputStream();
// The output writer
DataOutputStream data = new DataOutputStream(output);
// Request a server information packet
this.writePacket(data, new HandshakePacket(this.protocolVersion, address.getHostName(), address.getPort(), 1));
this.writePacket(data, new RequestPacket());
data.flush();
// Read a single packet, and close the connection
SimplePacket packet = this.readPacket(new DataInputStream(input), Protocol.STATUS);
socket.close();
return ((ResponsePacket) packet).getPingJson();
} finally {
if (input != null) {
input.close();
}
if (output != null) {
output.close();
}
if (socket != null) {
socket.close();
}
}
}
private void writePacket(DataOutputStream output, SimplePacket packet) throws IOException {
ByteArrayOutputStream packetBuffer = new ByteArrayOutputStream();
DataOutputStream packetOutput = new DataOutputStream(packetBuffer);
// Prefix the packet with a length field
packet.write(packetOutput);
writeByteArray(output, packetBuffer.toByteArray());
@@ -96,14 +127,15 @@ public class SimpleMinecraftClient {
private SimplePacket readPacket(DataInputStream input, Protocol protocol) throws IOException {
while (true) {
byte[] buffer = readByteArray(input);
// Skip empty packets
if (buffer.length == 0)
if (buffer.length == 0) {
continue;
DataInputStream data = getDataInput(buffer);
}
DataInputStream data = this.getDataInput(buffer);
PacketType type = PacketType.findCurrent(protocol, Sender.SERVER, serializer.deserializeVarInt(data));
if (type == PacketType.Status.Server.SERVER_INFO) {
ResponsePacket response = new ResponsePacket();
response.read(type, data);
@@ -116,69 +148,47 @@ public class SimpleMinecraftClient {
/**
* Wrap an input stream around a byte array.
*
* @param bytes - the array.
* @return The wrapped input stream.
*/
private DataInputStream getDataInput(byte[] bytes) {
return new DataInputStream(new ByteArrayInputStream(bytes));
}
/**
* Write a byte array to the output stream, prefixed by a length.
* @param output - the stream.
* @param data - the data to write.
*/
private static void writeByteArray(DataOutputStream output, byte[] data) throws IOException {
StreamSerializer.getDefault().serializeVarInt(output, data.length);
if (data.length > 0) {
output.write(data);
}
}
/**
* Read a byte array from an input stream, prefixed by length.
* @param input - the input stream.
* @return The read byte array.
*/
private static byte[] readByteArray(DataInputStream input) throws IOException {
int length = serializer.deserializeVarInt(input);
byte[] data = new byte[length];
ByteStreams.readFully(input, data);
return data;
}
private static class RequestPacket extends SimplePacket {
public RequestPacket() {
super(PacketType.Status.Client.START);
}
}
private static class ResponsePacket extends SimplePacket {
private String ping;
public ResponsePacket() {
super(PacketType.Status.Server.SERVER_INFO);
}
@Override
public void read(PacketType type, DataInputStream input) throws IOException {
super.read(type, input);
ping = serializer.deserializeString(input, 32000);
this.ping = this.serializer.deserializeString(input, 32000);
}
public String getPingJson() {
return ping;
return this.ping;
}
}
private static class HandshakePacket extends SimplePacket {
private int protocol;
private String host;
private int port;
private int nextState;
private final int protocol;
private final String host;
private final int port;
private final int nextState;
public HandshakePacket(int protocol, String host, int port, int nextState) {
super(PacketType.Handshake.Client.SET_PROTOCOL);
this.protocol = protocol;
@@ -190,25 +200,26 @@ public class SimpleMinecraftClient {
@Override
public void write(DataOutputStream output) throws IOException {
super.write(output);
serializer.serializeVarInt(output, protocol);
serializer.serializeString(output, host);
output.writeShort(port);
serializer.serializeVarInt(output, nextState);
this.serializer.serializeVarInt(output, this.protocol);
this.serializer.serializeString(output, this.host);
output.writeShort(this.port);
this.serializer.serializeVarInt(output, this.nextState);
}
}
private static class SimplePacket {
protected final PacketType type;
protected final StreamSerializer serializer = StreamSerializer.getDefault();
public SimplePacket(PacketType type) {
this.type = type;
}
public void write(DataOutputStream output) throws IOException {
serializer.serializeVarInt(output, type.getCurrentId());
this.serializer.serializeVarInt(output, this.type.getCurrentId());
}
@SuppressWarnings("unused")
public void read(PacketType type, DataInputStream input) throws IOException {
// Note - we don't read the packet id
@@ -1,21 +1,20 @@
package com.comphenix.integration.protocol;
import static org.junit.Assert.assertEquals;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.bukkit.plugin.Plugin;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.bukkit.plugin.Plugin;
public class TestPingPacket {
// Current versions
private static final int PROTOCOL_VERSION = 4;
@@ -30,6 +29,7 @@ public class TestPingPacket {
/**
* Create a new test ping packet test.
*
* @return The new test.
*/
public static TestPingPacket newTest() {
@@ -38,17 +38,18 @@ public class TestPingPacket {
/**
* Invoked when the test should be started.
*
* @param plugin - the current plugin.
* @throws Throwable Anything went wrong.
*/
public void startTest(Plugin plugin) throws Throwable {
try {
String transmitted = testInterception(plugin).
get(TIMEOUT_PING_MS, TimeUnit.MILLISECONDS);
String transmitted = this.testInterception(plugin).
get(TIMEOUT_PING_MS, TimeUnit.MILLISECONDS);
// Make sure it's the same
System.out.println("Server string: " + transmitted);
assertEquals(source, transmitted);
assertEquals(this.source, transmitted);
} catch (ExecutionException e) {
throw e.getCause();
}
@@ -56,12 +57,12 @@ public class TestPingPacket {
private Future<String> testInterception(Plugin test) {
ProtocolLibrary.getProtocolManager().addPacketListener(
new PacketAdapter(test, PacketType.Status.Server.SERVER_INFO) {
@Override
public void onPacketSending(PacketEvent event) {
source = event.getPacket().getServerPings().read(0).toJson();
}
});
new PacketAdapter(test, PacketType.Status.Server.SERVER_INFO) {
@Override
public void onPacketSending(PacketEvent event) {
TestPingPacket.this.source = event.getPacket().getServerPings().read(0).toJson();
}
});
// Invoke the client on a separate thread
return Executors.newSingleThreadExecutor().submit(new Callable<String>() {
@@ -71,8 +72,9 @@ public class TestPingPacket {
String information = client.queryLocalPing();
// Wait for the listener to catch up
for (int i = 0; i < 1000 && (source == null); i++)
for (int i = 0; i < 1000 && (TestPingPacket.this.source == null); i++) {
Thread.sleep(1);
}
return information;
}
@@ -1,47 +1,45 @@
package com.comphenix.protocol;
import java.util.Collections;
import java.util.List;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.utility.Constants;
import net.minecraft.server.v1_16_R3.DispenserRegistry;
import net.minecraft.server.v1_16_R3.WorldServer;
import java.util.Collections;
import java.util.List;
import net.minecraft.SharedConstants;
import net.minecraft.core.IRegistry;
import net.minecraft.server.DispenserRegistry;
import net.minecraft.server.level.WorldServer;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_16_R3.CraftServer;
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_16_R3.util.Versioning;
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_18_R2.util.Versioning;
import org.spigotmc.SpigotWorldConfig;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Used to ensure that ProtocolLib and Bukkit is prepared to be tested.
*
* @author Kristian
*/
public class BukkitInitialization {
private static final BukkitInitialization instance = new BukkitInitialization();
private boolean initialized;
private boolean packaged;
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() {
/**
* Statically initializes the mock server for unit testing
*/
public static synchronized void initializeAll() {
instance.initialize();
}
@@ -49,11 +47,9 @@ public class BukkitInitialization {
* Initialize Bukkit and ProtocolLib such that we can perfrom unit testing
*/
private void initialize() {
if (!initialized) {
if (!this.initialized) {
// Denote that we're done
initialized = true;
initializePackage();
this.initialized = true;
try {
LogManager.getLogger();
@@ -62,14 +58,26 @@ public class BukkitInitialization {
ex.printStackTrace();
}
DispenserRegistry.init();
instance.setPackage();
SharedConstants.a();
DispenserRegistry.a();
try {
IRegistry.class.getName();
} catch (Throwable ex) {
ex.printStackTrace();
}
String releaseTarget = SharedConstants.b().getReleaseTarget();
String serverVersion = CraftServer.class.getPackage().getImplementationVersion();
// Mock the server object
Server mockedServer = mock(Server.class);
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.getVersion()).thenReturn(serverVersion + " (MC: " + releaseTarget + ")");
when(mockedServer.getBukkitVersion()).thenReturn(Versioning.getBukkitVersion());
when(mockedServer.getItemFactory()).thenReturn(CraftItemFactory.instance());
@@ -100,8 +108,8 @@ public class BukkitInitialization {
* Ensure that package names are correctly set up.
*/
private void setPackage() {
if (!packaged) {
packaged = true;
if (!this.packaged) {
this.packaged = true;
try {
LogManager.getLogger();
@@ -2,39 +2,38 @@
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.Test;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftVersion;
import org.junit.jupiter.api.Test;
public class MinecraftVersionTest {
@Test
public void testComparision() {
MinecraftVersion within = new MinecraftVersion(1, 2, 5);
MinecraftVersion outside = new MinecraftVersion(1, 7, 0);
MinecraftVersion lower = new MinecraftVersion(1, 0, 0);
MinecraftVersion highest = new MinecraftVersion(1, 4, 5);
MinecraftVersion atLeast = new MinecraftVersion(1, 8, 8);
// Make sure this is valid
@@ -54,8 +53,9 @@ public class MinecraftVersionTest {
assertEquals(MinecraftVersion.extractVersion("CraftBukkit R3.0 (MC: 1.4.3)"), "1.4.3");
assertEquals(MinecraftVersion.extractVersion("CraftBukkit Test Beta 1 (MC: 1.10.01 )"), "1.10.01");
assertEquals(MinecraftVersion.extractVersion("Hello (MC: 2.3.4)"), "2.3.4");
assertEquals(MinecraftVersion.fromServerVersion("git-Cauldron-Reloaded-1.7.10-1.1388.1.0 (MC: 1.7.10)"), new MinecraftVersion(1, 7, 10));
assertEquals(MinecraftVersion.fromServerVersion("git-Cauldron-Reloaded-1.7.10-1.1388.1.0 (MC: 1.7.10)"),
new MinecraftVersion(1, 7, 10));
assertEquals(MinecraftVersion.fromServerVersion("git-Bukkit-18fbb24 (MC: 1.8.8)"), new MinecraftVersion(1, 8, 8));
}
}
@@ -1,59 +1,63 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 dmulloy2
* 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.
* 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.
* 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
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.comphenix.protocol;
import java.lang.reflect.Field;
import java.util.*;
import java.util.Map.Entry;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.Constants;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import net.minecraft.server.v1_16_R3.EnumProtocol;
import net.minecraft.server.v1_16_R3.EnumProtocolDirection;
import net.minecraft.server.v1_16_R3.PacketLoginInStart;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import net.minecraft.network.EnumProtocol;
import net.minecraft.network.protocol.EnumProtocolDirection;
import net.minecraft.network.protocol.login.PacketLoginInStart;
import org.apache.commons.lang.WordUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author dmulloy2
*/
public class PacketTypeTest {
@BeforeClass
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
// I'm well aware this is jank, but it does in fact work correctly and give the desired result
PacketType.onDynamicCreate = className -> {
throw new RuntimeException("Dynamically generated packet " + className);
};
}
@AfterClass
@AfterAll
public static void afterClass() {
PacketType.onDynamicCreate = __ -> {};
PacketType.onDynamicCreate = __ -> {
};
}
@SuppressWarnings("unchecked")
@@ -67,7 +71,7 @@ public class PacketTypeTest {
for (EnumProtocol protocol : protocols) {
System.out.println(WordUtils.capitalize(protocol.name().toLowerCase()));
Field field = EnumProtocol.class.getDeclaredField("h");
Field field = EnumProtocol.class.getDeclaredField("j");
field.setAccessible(true);
Map<EnumProtocolDirection, Object> map = (Map<EnumProtocolDirection, Object>) field.get(protocol);
@@ -144,17 +148,30 @@ public class PacketTypeTest {
String fullName = clazz.getName();
fullName = fullName.substring(fullName.lastIndexOf(".") + 1);
List<String> classNames = new ArrayList<>(2);
for (String name : fullName.split("\\$")) {
List<String> split = splitOnCaps(name);
StringBuilder nameBuilder = new StringBuilder();
for (int i = 3; i < split.size(); i++) {
nameBuilder.append(split.get(i));
String className;
List<String> classNames = new ArrayList<>();
if (fullName.endsWith("Packet")) {
for (String name : fullName.split("\\$")) {
List<String> split = splitOnCaps(name);
StringBuilder nameBuilder = new StringBuilder();
for (int i = 1; i < split.size() - 1; i++) {
nameBuilder.append(split.get(i));
}
classNames.add(nameBuilder.toString());
}
} else {
for (String name : fullName.split("\\$")) {
List<String> split = splitOnCaps(name);
StringBuilder nameBuilder = new StringBuilder();
for (int i = 3; i < split.size(); i++) {
nameBuilder.append(split.get(i));
}
classNames.add(nameBuilder.toString());
}
classNames.add(nameBuilder.toString());
}
String className = classNames.get(classNames.size() - 1);
className = classNames.get(classNames.size() - 1);
// Format it like SET_PROTOCOL
StringBuilder fieldName = new StringBuilder();
@@ -208,11 +225,13 @@ public class PacketTypeTest {
try {
PacketType type = PacketType.fromClass(packetClass);
for (String alias : type.names) {
alias = alias.substring(alias.lastIndexOf('.') + 1);
if (!names.contains(alias)) {
names.add(alias);
}
}
} catch (Exception ignored) { }
} catch (Exception ignored) {
}
return names;
}
@@ -229,14 +248,15 @@ public class PacketTypeTest {
return builder.toString();
}
@BeforeClass
@BeforeAll
public static void initializeReflection() {
BukkitInitialization.initializePackage();
BukkitInitialization.initializeAll();
}
@Test
public void testFindCurrent() {
assertEquals(PacketType.Play.Client.STEER_VEHICLE, PacketType.findCurrent(Protocol.PLAY, Sender.CLIENT, "SteerVehicle"));
assertEquals(PacketType.Play.Client.STEER_VEHICLE,
PacketType.findCurrent(Protocol.PLAY, Sender.CLIENT, "SteerVehicle"));
}
@Test
@@ -247,12 +267,12 @@ public class PacketTypeTest {
@Test
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);
assertTrue(PacketType.Status.Server.OUT_SERVER_INFO.isDeprecated(), "Packet isn't properly deprecated");
assertTrue(PacketRegistry.getServerPacketTypes().contains(PacketType.Status.Server.OUT_SERVER_INFO),
"Deprecated packet isn't properly included");
assertFalse(PacketType.Play.Server.CHAT.isDeprecated(), "Packet isn't properly deprecated");
assertEquals(PacketType.Status.Server.OUT_SERVER_INFO, PacketType.Status.Server.SERVER_INFO,
"Deprecated packets aren't equal");
}
@Test
@@ -262,12 +282,12 @@ public class PacketTypeTest {
EnumProtocol[] protocols = EnumProtocol.values();
for (EnumProtocol protocol : protocols) {
Field field = EnumProtocol.class.getDeclaredField("h");
Field field = EnumProtocol.class.getDeclaredField("j");
field.setAccessible(true);
Map<EnumProtocolDirection, Object> map = (Map<EnumProtocolDirection, Object>) field.get(protocol);
for (Entry<EnumProtocolDirection, Object> entry : map.entrySet()) {
Field mapField = entry.getValue().getClass().getDeclaredField("a");
Field mapField = entry.getValue().getClass().getDeclaredField("b");
mapField.setAccessible(true);
Map<Class<?>, Integer> reverseMap = (Map<Class<?>, Integer>) mapField.get(entry.getValue());
@@ -280,9 +300,11 @@ public class PacketTypeTest {
for (Entry<Integer, Class<?>> entry1 : treeMap.entrySet()) {
try {
PacketType type = PacketType.fromClass(entry1.getValue());
if (type.getCurrentId() != entry1.getKey())
if (type.getCurrentId() != entry1.getKey()) {
throw new IllegalStateException(
"Packet ID for " + type + " is incorrect. Expected " + entry1.getKey() + ", but got " + type.getCurrentId());
"Packet ID for " + type + " is incorrect. Expected " + entry1.getKey() + ", but got "
+ type.getCurrentId());
}
} catch (Throwable ex) {
ex.printStackTrace();
fail = true;
@@ -291,6 +313,22 @@ public class PacketTypeTest {
}
}
assertTrue("Packet type(s) were incorrect!", !fail);
assertFalse(fail, "Packet type(s) were incorrect!");
}
@Test
public void testPacketCreation() {
boolean fail = false;
for (PacketType type : PacketType.values()) {
if (type.isSupported()) {
try {
new PacketContainer(type);
} catch (Exception ex) {
ex.printStackTrace();
fail = true;
}
}
}
assertFalse(fail, "Packet type(s) failed to instantiate");
}
}
@@ -2,30 +2,28 @@
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.concurrency;
import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.junit.Test;
import org.junit.jupiter.api.Test;
public class BlockingHashMapTest {
@@ -35,25 +33,21 @@ public class BlockingHashMapTest {
final BlockingHashMap<Integer, String> map = BlockingHashMap.create();
ExecutorService service = Executors.newSingleThreadExecutor();
// Create a reader
Future<String> future = service.submit(new Callable<String>() {
@Override
public String call() throws Exception {
// Combine for easy reading
return map.get(0) + map.get(1);
}
Future<String> future = service.submit(() -> {
// Combine for easy reading
return map.get(0) + map.get(1);
});
// Wait a bit
Thread.sleep(50);
// Insert values
map.put(0, "hello ");
map.put(1, "world");
// Wait for the other thread to complete
assertEquals(future.get(), "hello world");
}
}
@@ -1,24 +1,31 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2012 Kristian S.
* Stangeland
* <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.events;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;
import static com.comphenix.protocol.utility.TestUtils.assertItemCollectionsEqual;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static com.comphenix.protocol.utility.TestUtils.equivalentItem;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
@@ -28,20 +35,48 @@ 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.BukkitConverters;
import com.comphenix.protocol.wrappers.ComponentConverter;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
import com.comphenix.protocol.wrappers.MovingObjectPositionBlock;
import com.comphenix.protocol.wrappers.Pair;
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.WrappedEnumEntityUseAction;
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 net.md_5.bungee.api.chat.*;
import net.minecraft.server.v1_16_R3.*;
import net.minecraft.server.v1_16_R3.MinecraftKey;
import net.minecraft.server.v1_16_R3.PacketPlayOutUpdateAttributes.AttributeSnapshot;
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.Optional;
import java.util.UUID;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import net.minecraft.core.IRegistry;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectList;
import net.minecraft.world.entity.ai.attributes.AttributeBase;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.npc.VillagerData;
import net.minecraft.world.entity.npc.VillagerProfession;
import net.minecraft.world.entity.npc.VillagerType;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.bukkit.ChatColor;
@@ -54,34 +89,26 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import static com.comphenix.protocol.utility.TestUtils.*;
import static org.junit.Assert.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
// Ensure that the CraftItemFactory is mockable
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*", "javax.management.*", "javax.xml.parsers.*", "com.sun.org.apache.xerces.internal.jaxp.*" })
//@PrepareForTest(CraftItemFactory.class)
public class PacketContainerTest {
// Helper converters
private EquivalentConverter<WrappedDataWatcher> watchConvert = BukkitConverters.getDataWatcherConverter();
private EquivalentConverter<ItemStack> itemConvert = BukkitConverters.getItemStackConverter();
private static BaseComponent[] TEST_COMPONENT;
// Helper converters
private final EquivalentConverter<WrappedDataWatcher> watchConvert = BukkitConverters.getDataWatcherConverter();
private final EquivalentConverter<ItemStack> itemConvert = BukkitConverters.getItemStackConverter();
@BeforeClass
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeItemMeta();
BukkitInitialization.initializePackage();
BukkitInitialization.initializeAll();
TEST_COMPONENT =
new ComponentBuilder("Hit or miss?")
.event(new ClickEvent(ClickEvent.Action.OPEN_URL, "http://reddit.com"))
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new BaseComponent[] { new TextComponent("The \"front page\" of the internet") }))
.append("I guess they never miss, huh?").create();
TEST_COMPONENT = new ComponentBuilder("Hit or miss?")
.event(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://reddit.com"))
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("The \"front page\" of the internet")))
.append("I guess they never miss, huh?")
.create();
}
private <T> void testPrimitive(StructureModifier<T> modifier, int index, T initialValue, T testValue) {
@@ -95,7 +122,7 @@ public class PacketContainerTest {
private <T> void testObjectArray(StructureModifier<T[]> modifier, int index, T[] initialValue, T[] testValue) {
// Check initial value
assertNull(modifier.read(index));
assertEquals(modifier.read(index).length, initialValue.length);
modifier.writeDefaults();
// Test initial
@@ -111,7 +138,7 @@ public class PacketContainerTest {
// Contains a byte array we will test
PacketContainer customPayload = new PacketContainer(PacketType.Login.Client.ENCRYPTION_BEGIN);
StructureModifier<byte[]> bytes = customPayload.getByteArrays();
byte[] testArray = new byte[] { 1, 2, 3 };
byte[] testArray = new byte[]{1, 2, 3};
// It's NULL at first
// assertEquals(null, bytes.read(0));
@@ -128,61 +155,61 @@ public class PacketContainerTest {
@Test
public void testGetBytes() {
PacketContainer spawnMob = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY_LIVING);
testPrimitive(spawnMob.getBytes(), 0, (byte)0, (byte)1);
this.testPrimitive(spawnMob.getBytes(), 0, (byte) 0, (byte) 1);
}
@Test
public void testGetShorts() {
PacketContainer itemData = new PacketContainer(PacketType.Play.Server.TRANSACTION);
testPrimitive(itemData.getShorts(), 0, (short)0, (short)1);
PacketContainer itemData = new PacketContainer(PacketType.Play.Server.REL_ENTITY_MOVE);
this.testPrimitive(itemData.getShorts(), 0, (short) 0, (short) 1);
}
@Test
public void testGetIntegers() {
PacketContainer updateSign = new PacketContainer(PacketType.Play.Client.CLOSE_WINDOW);
testPrimitive(updateSign.getIntegers(), 0, 0, 1);
this.testPrimitive(updateSign.getIntegers(), 0, 0, 1);
}
@Test
public void testGetLongs() {
PacketContainer updateTime = new PacketContainer(PacketType.Play.Server.UPDATE_TIME);
testPrimitive(updateTime.getLongs(), 0, (long)0, (long)1);
this.testPrimitive(updateTime.getLongs(), 0, (long) 0, (long) 1);
}
@Test
public void testGetFloat() {
PacketContainer explosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
testPrimitive(explosion.getFloat(), 0, (float)0, (float)0.8);
this.testPrimitive(explosion.getFloat(), 0, (float) 0, (float) 0.8);
}
@Test
public void testGetDoubles() {
PacketContainer explosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
testPrimitive(explosion.getDoubles(), 0, (double)0, 0.8);
this.testPrimitive(explosion.getDoubles(), 0, (double) 0, 0.8);
}
@Test
public void testGetStrings() {
PacketContainer explosion = new PacketContainer(PacketType.Play.Client.CHAT);
testPrimitive(explosion.getStrings(), 0, null, "hello");
this.testPrimitive(explosion.getStrings(), 0, "", "hello");
}
@Test
public void testGetStringArrays() {
PacketContainer packet = new PacketContainer(PacketType.Play.Client.UPDATE_SIGN);
testObjectArray(packet.getStringArrays(), 0, new String[0], new String[] { "hello", "world" });
this.testObjectArray(packet.getStringArrays(), 0,
new String[]{"", "", "", ""},
new String[]{"hello", "world"}
);
}
@Test
public void testGetIntegerArrays() {
// Contains a byte array we will test
PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY);
PacketContainer packet = new PacketContainer(PacketType.Play.Server.MOUNT);
StructureModifier<int[]> integers = packet.getIntegerArrays();
int[] testArray = new int[] { 1, 2, 3 };
int[] testArray = new int[]{1, 2, 3};
// Pre and post conditions
assertArrayEquals(null, integers.read(0));
packet.getModifier().writeDefaults();
assertArrayEquals(new int[0], integers.read(0));
integers.write(0, testArray);
@@ -193,14 +220,14 @@ public class PacketContainerTest {
public void testGetItemModifier() {
PacketContainer windowClick = new PacketContainer(PacketType.Play.Client.WINDOW_CLICK);
ItemStack item = itemWithData();
ItemStack item = this.itemWithData();
StructureModifier<ItemStack> items = windowClick.getItemModifier();
// assertNull(items.read(0));
// Insert the item and check if it's there
items.write(0, item);
assertTrue("Item " + item + " != " + items.read(0), equivalentItem(item, items.read(0)));
assertTrue(equivalentItem(item, items.read(0)), "Item " + item + " != " + items.read(0));
}
private ItemStack itemWithData() {
@@ -218,10 +245,10 @@ public class PacketContainerTest {
StructureModifier<List<ItemStack>> itemAccess = windowItems.getItemListModifier();
List<ItemStack> items = new ArrayList<>();
items.add(itemWithData());
items.add(this.itemWithData());
items.add(new ItemStack(Material.DIAMOND_AXE));
assertNull(itemAccess.read(0));
assertEquals(itemAccess.read(0).size(), 0);
// Insert and check that it was succesful
itemAccess.write(0, items);
@@ -234,8 +261,9 @@ public class PacketContainerTest {
@Test
public void testGetWorldTypeModifier() {
// Not used in Netty
if (MinecraftReflection.isUsingNetty())
if (MinecraftReflection.isUsingNetty()) {
return;
}
PacketContainer loginPacket = new PacketContainer(PacketType.Play.Server.LOGIN);
StructureModifier<WorldType> worldAccess = loginPacket.getWorldTypeModifier();
@@ -307,7 +335,7 @@ public class PacketContainerTest {
PacketContainer explosionPacket = new PacketContainer(PacketType.Play.Server.EXPLOSION);
StructureModifier<List<BlockPosition>> positionAccessor = explosionPacket.getBlockPositionCollectionModifier();
assertNull(positionAccessor.read(0));
assertEquals(positionAccessor.read(0).size(), 0);
List<BlockPosition> positions = Lists.newArrayList();
positions.add(new BlockPosition(1, 2, 3));
@@ -346,7 +374,8 @@ public class PacketContainerTest {
@Test
public void testGameProfiles() {
PacketContainer spawnEntity = new PacketContainer(PacketType.Login.Server.SUCCESS);
WrappedGameProfile profile = new WrappedGameProfile(UUID.fromString("d7047a08-3150-4aa8-a2f2-7c1e2b17e298"), "name");
WrappedGameProfile profile = new WrappedGameProfile(UUID.fromString("d7047a08-3150-4aa8-a2f2-7c1e2b17e298"),
"name");
spawnEntity.getGameProfiles().write(0, profile);
assertEquals(profile, spawnEntity.getGameProfiles().read(0));
@@ -359,7 +388,7 @@ public class PacketContainerTest {
WrappedChatComponent.fromChatMessage("You shall not " + ChatColor.ITALIC + "pass!")[0]);
assertEquals("{\"extra\":[{\"text\":\"You shall not \"},{\"italic\":true,\"text\":\"pass!\"}],\"text\":\"\"}",
chatPacket.getChatComponents().read(0).getJson());
chatPacket.getChatComponents().read(0).getJson());
}
@Test
@@ -373,6 +402,19 @@ public class PacketContainerTest {
assertEquals("Test", copy.getStrings().read(0));
}
@Test
public void testIntList() {
PacketContainer destroy = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY);
destroy.getIntLists().write(0, new ArrayList<Integer>() {{
this.add(420);
this.add(69);
}});
List<Integer> back = destroy.getIntLists().read(0);
assertEquals(back.size(), 2);
assertEquals((int) back.get(0), 420);
assertEquals((int) back.get(1), 69);
}
@Test
public void testAttributeList() {
PacketContainer attribute = new PacketContainer(PacketType.Play.Server.UPDATE_ATTRIBUTES);
@@ -380,18 +422,20 @@ public class PacketContainerTest {
// Initialize some test data
List<AttributeModifier> modifiers = Lists.newArrayList(
new AttributeModifier(UUID.randomUUID(), "Unknown synced attribute modifier", 10, AttributeModifier.Operation.ADDITION));
new AttributeModifier(UUID.randomUUID(), "Unknown synced attribute modifier", 10,
AttributeModifier.Operation.a));
// Obtain an AttributeSnapshot instance. This is complicated by the fact that AttributeSnapshots
// are inner classes (which is ultimately pointless because AttributeSnapshots don't access any
// members of the packet itself)
PacketPlayOutUpdateAttributes packet = (PacketPlayOutUpdateAttributes) attribute.getHandle();
AttributeBase base = IRegistry.ATTRIBUTE.get(MinecraftKey.a("generic.max_health"));
AttributeSnapshot snapshot = packet.new AttributeSnapshot(base, 20.0D, modifiers);
AttributeBase base = IRegistry.aj.a(MinecraftKey.a("generic.max_health"));
AttributeSnapshot snapshot = new AttributeSnapshot(base, 20.0D, modifiers);
attribute.getSpecificModifier(List.class).write(0, Lists.newArrayList(snapshot));
PacketContainer cloned = attribute.deepClone();
AttributeSnapshot clonedSnapshot = (AttributeSnapshot) cloned.getSpecificModifier(List.class).read(0).get(0);
AttributeSnapshot
clonedSnapshot = (AttributeSnapshot) cloned.getSpecificModifier(List.class).read(0).get(0);
// Compare the fields, because apparently the packet is a field in AttributeSnapshot
for (Field field : AttributeSnapshot.class.getDeclaredFields()) {
@@ -402,7 +446,7 @@ public class PacketContainerTest {
}
field.setAccessible(true);
assertEquals(field.get(snapshot), field.get(clonedSnapshot));
this.testEquality(field.get(snapshot), field.get(clonedSnapshot));
} catch (AssertionError e) {
throw e;
} catch (Throwable ex) {
@@ -435,26 +479,33 @@ public class PacketContainerTest {
@SuppressWarnings("deprecation")
public void testPotionEffect() {
PotionEffect effect = new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 20 * 60, 1);
MobEffect mobEffect = new MobEffect(MobEffectList.fromId(effect.getType().getId()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(),
MobEffect mobEffect = new MobEffect(MobEffectList.a(effect.getType().getId()), effect.getDuration(),
effect.getAmplifier(), effect.isAmbient(),
effect.hasParticles());
int entityId = 42;
// The constructor we want to call
PacketConstructor creator = PacketConstructor.DEFAULT.withPacket(
PacketType.Play.Server.ENTITY_EFFECT, new Class<?>[] { int.class, MobEffect.class });
PacketType.Play.Server.ENTITY_EFFECT, new Class<?>[]{int.class, MobEffect.class});
PacketContainer packet = creator.createPacket(entityId, mobEffect);
assertEquals(entityId, (int) packet.getIntegers().read(0));
assertEquals(effect.getType().getId(), (byte) packet.getBytes().read(0));
assertEquals(effect.getAmplifier(), (byte) packet.getBytes().read(1));
assertEquals(effect.getDuration(), (int) packet.getIntegers().read(1));
assertEquals(entityId, packet.getIntegers().read(0));
assertEquals(effect.getType().getId(), packet.getIntegers().read(1));
assertEquals(effect.getAmplifier(), (byte) packet.getBytes().read(0));
assertEquals(effect.getDuration(), packet.getIntegers().read(2));
int e = 0;
if (effect.isAmbient()) e |= 1;
if (effect.hasParticles()) e |= 2;
if (effect.hasIcon()) e |= 4;
if (effect.isAmbient()) {
e |= 1;
}
if (effect.hasParticles()) {
e |= 2;
}
if (effect.hasIcon()) {
e |= 4;
}
assertEquals(e, (byte) packet.getBytes().read(2));
assertEquals(e, (byte) packet.getBytes().read(1));
}
@Test
@@ -494,16 +545,35 @@ public class PacketContainerTest {
assertEquals(container.getSoundEffects().read(0), Sound.ENTITY_CAT_HISS);
}
@Test
// @Test
public void testGenericEnums() {
PacketContainer container = new PacketContainer(PacketType.Play.Server.BOSS);
container.getEnumModifier(Action.class, 1).write(0, Action.UPDATE_PCT);
assertEquals(container.getEnumModifier(Action.class, PacketPlayOutBoss.Action.class).read(0), Action.UPDATE_PCT);
// assertEquals(container.getEnumModifier(Action.class, PacketPlayOutBoss.d.class).read(0), Action.UPDATE_PCT);
}
@Test
public void testInternalStructures() {
PacketContainer container = new PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM);
Optional<InternalStructure> optStruct = container.getOptionalStructures().read(0);
assertTrue(optStruct.isPresent());
InternalStructure struct = optStruct.get();
struct.getChatComponents().write(0, WrappedChatComponent.fromText("hi there"));
container.getOptionalStructures().write(0, Optional.of(struct));
optStruct = container.getOptionalStructures().read(0);
assertTrue(optStruct.isPresent());
struct = optStruct.get();
this.testEquality(
struct.getChatComponents().read(0),
WrappedChatComponent.fromText("hi there")
);
}
// @Test
public void testDimensions() {
// TODO this won't work in testing, but hopefully will in live
PacketContainer container = new PacketContainer(PacketType.Play.Server.RESPAWN);
container.getDimensions().write(0, 1);
assertEquals((Object) 1, container.getDimensions().read(0));
@@ -546,14 +616,14 @@ public class PacketContainerTest {
public void testMultiBlockChange() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.MULTI_BLOCK_CHANGE);
packet.getShortArrays().writeSafely(0, new short[] { 420, 69 });
assertArrayEquals(new short[] { 420, 69}, packet.getShortArrays().readSafely(0));
packet.getShortArrays().writeSafely(0, new short[]{420, 69});
assertArrayEquals(new short[]{420, 69}, packet.getShortArrays().readSafely(0));
packet.getBlockDataArrays().writeSafely(0, new WrappedBlockData[] {
packet.getBlockDataArrays().writeSafely(0, new WrappedBlockData[]{
WrappedBlockData.createData(Material.IRON_BARS),
WrappedBlockData.createData(Material.IRON_BLOCK)
});
assertArrayEquals(new WrappedBlockData[] {
assertArrayEquals(new WrappedBlockData[]{
WrappedBlockData.createData(Material.IRON_BARS),
WrappedBlockData.createData(Material.IRON_BLOCK)
}, packet.getBlockDataArrays().readSafely(0));
@@ -565,23 +635,80 @@ public class PacketContainerTest {
assertNotSame(clone, packet);
}
/**
* Actions from the outbound Boss packet. Used for testing generic enums.
* @author dmulloy2
*/
public enum Action {
ADD,
REMOVE,
UPDATE_PCT,
UPDATE_NAME,
UPDATE_STYLE,
UPDATE_PROPERTIES
@Test
public void testGameStateChange() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.GAME_STATE_CHANGE);
assertEquals(0, (int) packet.getGameStateIDs().read(0));
packet.getGameStateIDs().write(0, 2);
assertEquals(2, (int) packet.getGameStateIDs().read(0));
}
@Test
public void testUseEntity() {
PacketContainer packet = new PacketContainer(PacketType.Play.Client.USE_ENTITY);
WrappedEnumEntityUseAction action;
WrappedEnumEntityUseAction clone;
// test attack
packet.getEnumEntityUseActions().write(0, WrappedEnumEntityUseAction.attack());
action = packet.getEnumEntityUseActions().read(0);
// attack's handle should always be the same
assertEquals(WrappedEnumEntityUseAction.attack(), action);
assertEquals(EntityUseAction.ATTACK, action.getAction());
// hand & position should not be available
assertThrows(IllegalArgumentException.class, action::getHand);
assertThrows(IllegalArgumentException.class, action::getPosition);
// test cloning
clone = action.deepClone();
assertSame(WrappedEnumEntityUseAction.attack(), clone);
// test interact
packet.getEnumEntityUseActions().write(0, WrappedEnumEntityUseAction.interact(Hand.OFF_HAND));
action = packet.getEnumEntityUseActions().read(0);
assertEquals(EntityUseAction.INTERACT, action.getAction());
assertEquals(Hand.OFF_HAND, action.getHand());
// position should not be available
assertThrows(IllegalArgumentException.class, action::getPosition);
// test cloning
clone = action.deepClone();
assertEquals(EntityUseAction.INTERACT, clone.getAction());
assertEquals(Hand.OFF_HAND, clone.getHand());
// test interact_at
Vector position = new Vector(1, 199, 4);
packet.getEnumEntityUseActions().write(0, WrappedEnumEntityUseAction.interactAt(Hand.MAIN_HAND, position));
action = packet.getEnumEntityUseActions().read(0);
assertEquals(EntityUseAction.INTERACT_AT, action.getAction());
assertEquals(Hand.MAIN_HAND, action.getHand());
assertEquals(position, action.getPosition());
// test cloning
clone = action.deepClone();
assertEquals(EntityUseAction.INTERACT_AT, clone.getAction());
assertEquals(Hand.MAIN_HAND, clone.getHand());
assertEquals(position, clone.getPosition());
}
@Test
public void testSetSimulationDistance() {
// first packet which is a record - set will fail if we missed something during patching
PacketContainer container = new PacketContainer(PacketType.Play.Server.UPDATE_SIMULATION_DISTANCE);
container.getIntegers().write(0, 1234);
assertEquals(1234, (int) container.getIntegers().read(0));
}
@Test
public void testMapChunk() {
// this is a special case as we are generating a data serializer class (we only need to construct the packet)
PacketContainer container = new PacketContainer(PacketType.Play.Server.MAP_CHUNK);
// check if we can read an nbt compound from the class
assertTrue(container.getStructures().read(0).getNbtModifier().optionRead(0).isPresent());
}
@Test
public void testComponentArrays() {
PacketContainer signChange = new PacketContainer(PacketType.Play.Server.TILE_ENTITY_DATA);
WrappedChatComponent[] components = new WrappedChatComponent[] {
WrappedChatComponent[] components = new WrappedChatComponent[]{
WrappedChatComponent.fromText("hello world"), WrappedChatComponent.fromText(""),
WrappedChatComponent.fromText(""), WrappedChatComponent.fromText("")
};
@@ -591,12 +718,33 @@ public class PacketContainerTest {
assertArrayEquals(components, back);
}
private void assertPacketsEqual(PacketContainer constructed, PacketContainer cloned) {
StructureModifier<Object> firstMod = constructed.getModifier(), secondMod = cloned.getModifier();
assertEquals(firstMod.size(), secondMod.size());
if (PacketType.Status.Server.SERVER_INFO.equals(constructed.getType())) {
assertArrayEquals(SerializationUtils.serialize(constructed), SerializationUtils.serialize(cloned));
} else {
// Make sure all the fields are equivalent
for (int i = 0; i < firstMod.size(); i++) {
if (firstMod.getField(i).getType().isArray()) {
assertArrayEquals(this.getArray(firstMod.read(i)), this.getArray(secondMod.read(i)));
} else {
this.testEquality(firstMod.read(i), secondMod.read(i));
}
}
}
}
@Test
public void testDeepClone() {
public void testCloning() {
boolean failed = false;
// Try constructing all the packets
for (PacketType type : PacketType.values()) {
if (type.isDeprecated() || type.name().contains("CUSTOM_PAYLOAD") || type.name().contains("TAGS") || !type.isSupported()
|| type == PacketType.Play.Server.RECIPES) {
if (type.isDeprecated() || type.name().contains("CUSTOM_PAYLOAD") || type.name().contains("TAGS")
|| !type.isSupported()
|| type == PacketType.Play.Server.RECIPES) {
continue;
}
@@ -615,64 +763,66 @@ public class PacketContainerTest {
"String"),
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Float.class)), 1.0F),
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.getChatComponentSerializer(true)),
com.google.common.base.Optional.of(ComponentConverter.fromBaseComponent(TEST_COMPONENT).getHandle())),
Optional.of(ComponentConverter.fromBaseComponent(TEST_COMPONENT).getHandle())),
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(VillagerData.class)),
new VillagerData(VillagerType.SNOW, VillagerProfession.ARMORER, 69))
new VillagerData(VillagerType.b, VillagerProfession.c, 69))
));
} else if (type == PacketType.Play.Server.CHAT) {
constructed.getChatComponents().write(0, ComponentConverter.fromBaseComponent(TEST_COMPONENT));
//constructed.getModifier().write(1, TEST_COMPONENT);
}
// Clone the packet
PacketContainer cloned = constructed.deepClone();
// Clone the packet both ways
PacketContainer shallowCloned = constructed.shallowClone();
this.assertPacketsEqual(constructed, shallowCloned);
// Make sure they're equivalent
StructureModifier<Object> firstMod = constructed.getModifier(), secondMod = cloned.getModifier();
assertEquals(firstMod.size(), secondMod.size());
if (PacketType.Status.Server.SERVER_INFO.equals(type)) {
assertArrayEquals(SerializationUtils.serialize(constructed), SerializationUtils.serialize(cloned));
} else {
// Make sure all the fields are equivalent
for (int i = 0; i < firstMod.size(); i++) {
if (firstMod.getField(i).getType().isArray())
assertArrayEquals(getArray(firstMod.read(i)), getArray(secondMod.read(i)));
else
testEquality(firstMod.read(i), secondMod.read(i));
}
}
PacketContainer deepCloned = constructed.deepClone();
this.assertPacketsEqual(constructed, deepCloned);
} catch (Exception ex) {
throw new RuntimeException("Failed to serialize packet " + type, ex);
ex.printStackTrace();
failed = true;
}
}
assertFalse(failed, "Packet(s) failed to clone");
}
// Convert to objects that support equals()
private void testEquality(Object a, Object b) {
if (a != null && b != null) {
if (MinecraftReflection.isDataWatcher(a)) {
a = watchConvert.getSpecific(a);
b = watchConvert.getSpecific(b);
} else if (MinecraftReflection.isItemStack(a)) {
a = itemConvert.getSpecific(a);
b = itemConvert.getSpecific(b);
if (a == null) {
if (b == null) {
return;
} else {
throw new AssertionError("a was null, but b was not");
}
} else if (b == null) {
throw new AssertionError("a was not null, but b was null");
}
if (a instanceof Optional) {
if (b instanceof Optional) {
this.testEquality(((Optional<?>) a).orElse(null), ((Optional<?>) b).orElse(null));
return;
} else {
throw new AssertionError("a was optional, but b was not");
}
}
if (a instanceof ItemStack || b instanceof ItemStack) {
assertItemsEqual((ItemStack) a, (ItemStack) b);
if (a.equals(b) || Objects.equals(a, b) || this.stringEquality(a, b)) {
return;
}
if (a == null || b == null) {
if (a == null && b == null) {
return;
}
} else {
if (a.equals(b) || Objects.equals(a, b) || stringEquality(a, b)) {
return;
}
if (MinecraftReflection.isDataWatcher(a)) {
a = this.watchConvert.getSpecific(a);
b = this.watchConvert.getSpecific(b);
} else if (MinecraftReflection.isItemStack(a)) {
a = this.itemConvert.getSpecific(a);
b = this.itemConvert.getSpecific(b);
}
if (a instanceof ItemStack && b instanceof ItemStack) {
assertItemsEqual((ItemStack) a, (ItemStack) b);
return;
}
if (EqualsBuilder.reflectionEquals(a, b)) {
@@ -693,20 +843,38 @@ public class PacketContainerTest {
/**
* Get the underlying array as an object array.
*
* @param val - array wrapped as an Object.
* @return An object array.
*/
private Object[] getArray(Object val) {
if (val instanceof Object[])
if (val instanceof Object[]) {
return (Object[]) val;
if (val == null)
}
if (val == null) {
return null;
}
int arrlength = Array.getLength(val);
Object[] outputArray = new Object[arrlength];
for (int i = 0; i < arrlength; ++i)
for (int i = 0; i < arrlength; ++i) {
outputArray[i] = Array.get(val, i);
}
return outputArray;
}
/**
* Actions from the outbound Boss packet. Used for testing generic enums.
*
* @author dmulloy2
*/
public enum Action {
ADD,
REMOVE,
UPDATE_PCT,
UPDATE_NAME,
UPDATE_STYLE,
UPDATE_PROPERTIES
}
}
@@ -1,72 +1,70 @@
package com.comphenix.protocol.events;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;
import java.util.UUID;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.UUID;
import static org.junit.Assert.*;
import static org.mockito.Mockito.when;
public class SerializedOfflinePlayerTest {
@Mock
static OfflinePlayer offlinePlayer;
private static final String name = "playerName";
private static final long firstPlayed = 1000L;
private static final long lastPlayed = firstPlayed + 100L;
private static final boolean isOp = false;
private static final boolean playedBefore = true;
private static final boolean whitelisted = true;
@Mock
static OfflinePlayer offlinePlayer;
private static UUID uuid;
private static SerializedOfflinePlayer serializedOfflinePlayer;
private static final String name = "playerName";
private static UUID uuid;
private static final long firstPlayed = 1000L;
private static final long lastPlayed = firstPlayed + 100L;
private static final boolean isOp = false;
private static final boolean playedBefore = true;
private static final boolean whitelisted = true;
@BeforeEach
public void initMocks() {
MockitoAnnotations.openMocks(this);
private static SerializedOfflinePlayer serializedOfflinePlayer;
uuid = UUID.randomUUID();
when(offlinePlayer.getName()).thenReturn(name);
when(offlinePlayer.getUniqueId()).thenReturn(uuid);
when(offlinePlayer.getFirstPlayed()).thenReturn(firstPlayed);
when(offlinePlayer.getLastPlayed()).thenReturn(lastPlayed);
when(offlinePlayer.isOp()).thenReturn(isOp);
when(offlinePlayer.hasPlayedBefore()).thenReturn(playedBefore);
when(offlinePlayer.isWhitelisted()).thenReturn(whitelisted);
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
serializedOfflinePlayer = new SerializedOfflinePlayer(offlinePlayer);
}
uuid = UUID.randomUUID();
when(offlinePlayer.getName()).thenReturn(name);
when(offlinePlayer.getUniqueId()).thenReturn(uuid);
when(offlinePlayer.getFirstPlayed()).thenReturn(firstPlayed);
when(offlinePlayer.getLastPlayed()).thenReturn(lastPlayed);
when(offlinePlayer.isOp()).thenReturn(isOp);
when(offlinePlayer.hasPlayedBefore()).thenReturn(playedBefore);
when(offlinePlayer.isWhitelisted()).thenReturn(whitelisted);
@Test
public void getProxyPlayer() {
Player player = serializedOfflinePlayer.getProxyPlayer();
assertNotNull(player);
serializedOfflinePlayer = new SerializedOfflinePlayer(offlinePlayer);
}
// getDisplayName only works for online players.
assertThrows(UnsupportedOperationException.class, player::getDisplayName);
@Test
public void getProxyPlayer() {
Player player = serializedOfflinePlayer.getProxyPlayer();
Assert.assertNotNull(player);
assertEquals(uuid, player.getUniqueId());
assertEquals(name, player.getName());
assertEquals(firstPlayed, player.getFirstPlayed());
assertEquals(lastPlayed, player.getLastPlayed());
assertEquals(isOp, player.isOp());
assertEquals(playedBefore, player.hasPlayedBefore());
assertEquals(whitelisted, player.isWhitelisted());
}
// getDisplayName only works for online players.
assertThrows(UnsupportedOperationException.class, player::getDisplayName);
@Test
public void getSecondProxyPlayer() {
// Make sure that the proxyPlayer generation doesn't work only once.
Player player = serializedOfflinePlayer.getProxyPlayer();
assertNotNull(player);
assertEquals(uuid, player.getUniqueId());
assertEquals(name, player.getName());
assertEquals(firstPlayed, player.getFirstPlayed());
assertEquals(lastPlayed, player.getLastPlayed());
assertEquals(isOp, player.isOp());
assertEquals(playedBefore, player.hasPlayedBefore());
assertEquals(whitelisted, player.isWhitelisted());
}
@Test
public void getSecondProxyPlayer() {
// Make sure that the proxyPlayer generation doesn't work only once.
Player player = serializedOfflinePlayer.getProxyPlayer();
Assert.assertNotNull(player);
assertEquals(uuid, player.getUniqueId());
}
assertEquals(uuid, player.getUniqueId());
}
}
@@ -1,30 +1,31 @@
package com.comphenix.protocol.injector;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.accessors.Accessors;
import net.minecraft.server.v1_16_R3.ChunkProviderServer;
import net.minecraft.server.v1_16_R3.Entity;
import net.minecraft.server.v1_16_R3.PlayerChunkMap;
import net.minecraft.server.v1_16_R3.PlayerChunkMap.EntityTracker;
import net.minecraft.server.v1_16_R3.WorldServer;
import org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftEntity;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static com.comphenix.protocol.utility.TestUtils.setFinalField;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.lang.reflect.Field;
import net.minecraft.server.level.ChunkProviderServer;
import net.minecraft.server.level.PlayerChunkMap;
import net.minecraft.server.level.PlayerChunkMap.EntityTracker;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.entity.Entity;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class EntityUtilitiesTest {
@BeforeClass
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeItemMeta();
BukkitInitialization.initializeAll();
}
@Test
@@ -34,21 +35,27 @@ public class EntityUtilitiesTest {
when(bukkit.getHandle()).thenReturn(world);
ChunkProviderServer provider = mock(ChunkProviderServer.class);
when(world.getChunkProvider()).thenReturn(provider);
when(world.k()).thenReturn(provider);
PlayerChunkMap chunkMap = mock(PlayerChunkMap.class);
Accessors.getFieldAccessor(ChunkProviderServer.class, "playerChunkMap", true).set(provider, chunkMap);
Field chunkMapField = FuzzyReflection.fromClass(ChunkProviderServer.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(PlayerChunkMap.class).build());
setFinalField(provider, chunkMapField, chunkMap);
CraftEntity bukkitEntity = mock(CraftEntity.class);
Entity fakeEntity = mock(Entity.class);
when(fakeEntity.getBukkitEntity()).thenReturn(bukkitEntity);
EntityTracker tracker = mock(EntityTracker.class);
Accessors.getFieldAccessor(EntityTracker.class, "tracker", true).set(tracker, fakeEntity);
Field trackerField = FuzzyReflection.fromClass(EntityTracker.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(Entity.class).build());
setFinalField(tracker, trackerField, fakeEntity);
Int2ObjectMap<EntityTracker> trackerMap = new Int2ObjectOpenHashMap<>();
trackerMap.put(1, tracker);
Accessors.getFieldAccessor(PlayerChunkMap.class, "trackedEntities", true).set(chunkMap, trackerMap);
Field trackedEntitiesField = FuzzyReflection.fromClass(PlayerChunkMap.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(Int2ObjectMap.class).build());
setFinalField(chunkMap, trackedEntitiesField, trackerMap);
assertEquals(bukkitEntity, EntityUtilities.getInstance().getEntityFromID(bukkit, 1));
}
@@ -1,53 +1,46 @@
package com.comphenix.protocol.injector;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.comphenix.protocol.injector.PluginVerifier.VerificationResult;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Server;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoadOrder;
import org.bukkit.plugin.PluginManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import com.comphenix.protocol.injector.PluginVerifier.VerificationResult;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
// Damn final classes
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
@PrepareForTest(PluginDescriptionFile.class)
public class PluginVerifierTest {
@Test
public void testDependecies() {
List<Plugin> plugins = Lists.newArrayList();
Server server = mockServer(plugins);
Plugin library = mockPlugin(server, "ProtocolLib", PluginLoadOrder.POSTWORLD);
Plugin skillPlugin = mockPlugin(server, "SkillPlugin", "RaidCraft-API", "RCPermissions", "RCConversations");
Plugin raidCraftAPI = mockPlugin(server, "RaidCraft-API", "WorldGuard", "WorldEdit");
Plugin conversations = mockPlugin(server, "RCConversations", "RaidCraft-API");
Plugin permissions = mockPlugin(server, "RCPermissions", "RaidCraft-API");
Server server = this.mockServer(plugins);
Plugin library = this.mockPlugin(server, "ProtocolLib", PluginLoadOrder.POSTWORLD);
Plugin skillPlugin = this.mockPlugin(server, "SkillPlugin", "RaidCraft-API", "RCPermissions", "RCConversations");
Plugin raidCraftAPI = this.mockPlugin(server, "RaidCraft-API", "WorldGuard", "WorldEdit");
Plugin conversations = this.mockPlugin(server, "RCConversations", "RaidCraft-API");
Plugin permissions = this.mockPlugin(server, "RCPermissions", "RaidCraft-API");
// Add the plugins
plugins.addAll(Arrays.asList(library, skillPlugin, raidCraftAPI, conversations, permissions));
PluginVerifier verifier = new PluginVerifier(library);
// Verify the root - it should have no dependencies on ProtocolLib
assertEquals(VerificationResult.NO_DEPEND, verifier.verify(skillPlugin));
}
private Server mockServer(final List<Plugin> plugins) {
Server mockServer = mock(Server.class);
PluginManager manager = mock(PluginManager.class);
@@ -57,7 +50,7 @@ public class PluginVerifierTest {
@Override
public Plugin answer(InvocationOnMock invocation) throws Throwable {
String name = (String) invocation.getArguments()[0];
for (Plugin plugin : plugins) {
if (Objects.equal(name, plugin.getName())) {
return plugin;
@@ -68,20 +61,20 @@ public class PluginVerifierTest {
});
return mockServer;
}
private Plugin mockPlugin(Server server, String name,String... depend) {
return mockPlugin(server, name, PluginLoadOrder.POSTWORLD, depend);
private Plugin mockPlugin(Server server, String name, String... depend) {
return this.mockPlugin(server, name, PluginLoadOrder.POSTWORLD, depend);
}
private Plugin mockPlugin(Server server, String name, PluginLoadOrder order, String... depend) {
Plugin plugin = mock(Plugin.class);
PluginDescriptionFile file = mock(PluginDescriptionFile.class);
when(plugin.getServer()).thenReturn(server);
when(plugin.getName()).thenReturn(name);
when(plugin.toString()).thenReturn(name);
when(plugin.getDescription()).thenReturn(file);
// This is the difficult part
when(file.getLoad()).thenReturn(order);
when(file.getDepend()).thenReturn(Arrays.asList(depend));
@@ -2,85 +2,84 @@
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import com.comphenix.protocol.concurrency.SortedCopyOnWriteArray;
import com.comphenix.protocol.events.ListenerPriority;
import com.google.common.primitives.Ints;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Test;
public class SortedCopyOnWriteArrayTest {
@Test
public void testInsertion() {
final int MAX_NUMBER = 50;
SortedCopyOnWriteArray<Integer> test = new SortedCopyOnWriteArray<Integer>();
// Generate some numbers
List<Integer> numbers = new ArrayList<Integer>();
for (int i = 0; i < MAX_NUMBER; i++) {
numbers.add(i);
}
// Random insertion to test it all
Collections.shuffle(numbers);
// O(n^2) of course, so don't use too large numbers
for (int i = 0; i < MAX_NUMBER; i++) {
test.add(numbers.get(i));
}
// Check that everything is correct
for (int i = 0; i < MAX_NUMBER; i++) {
assertEquals((Integer) i, test.get(i));
}
}
@Test
public void testOrder() {
PriorityStuff a = new PriorityStuff(ListenerPriority.HIGH, 1);
PriorityStuff b = new PriorityStuff(ListenerPriority.NORMAL, 2);
PriorityStuff c = new PriorityStuff(ListenerPriority.NORMAL, 3);
SortedCopyOnWriteArray<PriorityStuff> test = new SortedCopyOnWriteArray<PriorityStuff>();
test.add(a);
test.add(b);
test.add(c);
// Make sure the normal's are in the right order
assertEquals(2, test.get(0).id);
assertEquals(3, test.get(1).id);
// Test remove
test.remove(b);
assertEquals(2, test.size());
assertFalse(test.contains(b));
}
private static class PriorityStuff implements Comparable<PriorityStuff> {
public ListenerPriority priority;
public int id;
@@ -92,8 +91,8 @@ public class SortedCopyOnWriteArrayTest {
@Override
public int compareTo(PriorityStuff other) {
// This ensures that lower priority listeners are executed first
return Ints.compare(priority.getSlot(),
other.priority.getSlot());
return Ints.compare(this.priority.getSlot(),
other.priority.getSlot());
}
}
}
@@ -3,32 +3,27 @@
*/
package com.comphenix.protocol.injector;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
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;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author dmulloy2
*/
public class WirePacketTest {
@BeforeClass
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializePackage();
BukkitInitialization.initializeAll();
}
// @Test
@@ -36,8 +31,9 @@ public class WirePacketTest {
List<String> failures = new ArrayList<>();
for (PacketType type : PacketType.values()) {
if (type.isDeprecated())
if (type.isDeprecated()) {
continue;
}
try {
PacketContainer packet = new PacketContainer(type);
@@ -57,7 +53,7 @@ public class WirePacketTest {
@Test
public void testSerialization() {
int id = 42;
byte[] array = { 1, 3, 7, 21, 88, 67, 8 };
byte[] array = {1, 3, 7, 21, 88, 67, 8};
WirePacket packet = new WirePacket(id, array);
@@ -1,42 +1,42 @@
package com.comphenix.protocol.injector.server;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.Assert.*;
public class TemporaryPlayerFactoryTest {
private static final TemporaryPlayerFactory temporaryPlayerFactory = new TemporaryPlayerFactory();
private static final TemporaryPlayerFactory temporaryPlayerFactory = new TemporaryPlayerFactory();
@Mock
Server server;
@Mock
SocketInjector socketInjector;
@Mock
Server server;
@Mock
SocketInjector socketInjector;
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
@BeforeEach
public void initMocks() {
MockitoAnnotations.openMocks(this);
}
@Test
public void testUnavailableSocketInjector()
{
Player player = temporaryPlayerFactory.createTemporaryPlayer(server);
assertThrows(IllegalStateException.class, player::getPlayer);
}
@Test
public void testUnavailableSocketInjector() {
Player player = temporaryPlayerFactory.createTemporaryPlayer(this.server);
assertThrows(IllegalStateException.class, player::getPlayer);
}
@Test
public void createTemporaryPlayer() {
@Test
public void createTemporaryPlayer() {
Player player = temporaryPlayerFactory.createTemporaryPlayer(server, socketInjector);
assertEquals(server, player.getServer());
Player player = temporaryPlayerFactory.createTemporaryPlayer(this.server, this.socketInjector);
assertEquals(this.server, player.getServer());
// May seem dumb, but this makes sure that the .equals method is still instact.
assertEquals(player, player);
}
// May seem dumb, but this makes sure that the .equals method is still instact.
assertEquals(player, player);
}
}
@@ -1,52 +1,21 @@
package com.comphenix.protocol.reflect.accessors;
import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.Test;
import org.junit.jupiter.api.Test;
public class AccessorsTest {
// --- Some classes we can use for testing ---
private static class Entity {
private int id;
public Entity(int id) {
this.id = id;
}
public int getId() {
return id;
}
@SuppressWarnings("unused")
private void setId(int value) {
this.id = value;
}
}
private static class Player extends Entity {
private String name;
public Player(int id, String name) {
super(id);
this.name = name;
}
public String getName() {
return name;
}
}
// --- Test classes ---
@Test
public void testField() {
Player player = new Player(123, "ABC");
Accessors.getFieldAccessor(player.getClass(), "id", true).set(player, 0);
Accessors.getFieldAccessor(player.getClass(), "name", true).set(player, "MODIFIED");
assertEquals(0, player.getId());
assertEquals("MODIFIED", player.getName());
}
@Test
public void testMethod() {
Player player = new Player(123, "ABC");
@@ -54,4 +23,37 @@ public class AccessorsTest {
Accessors.getMethodAccessor(player.getClass(), "setId", int.class).invoke(player, 0);
assertEquals(0, player.getId());
}
// --- Some classes we can use for testing ---
private static class Entity {
private int id;
public Entity(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
@SuppressWarnings("unused")
private void setId(int value) {
this.id = value;
}
}
private static class Player extends Entity {
private final String name;
public Player(int id, String name) {
super(id);
this.name = name;
}
public String getName() {
return this.name;
}
}
}
@@ -1,35 +1,33 @@
package com.comphenix.protocol.reflect.cloning;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.List;
import net.minecraft.server.v1_16_R3.ItemStack;
import net.minecraft.server.v1_16_R3.NonNullList;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import java.util.Arrays;
import java.util.List;
import net.minecraft.core.NonNullList;
import net.minecraft.world.item.ItemStack;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class AggregateClonerTest {
@BeforeClass
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializePackage();
BukkitInitialization.initializeAll();
}
@Test
public void testArrays() {
public void testArrays() {
List<Integer> input = Arrays.asList(1, 2, 3);
assertEquals(input, AggregateCloner.DEFAULT.clone(input));
}
@Test
// @Test
// Usages of NonNullList were removed in 1.17.1
public void testNonNullList() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WINDOW_ITEMS);
@@ -42,6 +40,6 @@ public class AggregateClonerTest {
NonNullList<ItemStack> list1 = (NonNullList<ItemStack>) cloned.getModifier().read(1);
assertEquals(list.size(), list1.size());
assertArrayEquals(list.toArray(), list1.toArray());
Assertions.assertArrayEquals(list.toArray(), list1.toArray());
}
}
@@ -3,46 +3,47 @@
*/
package com.comphenix.protocol.updater;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.comphenix.protocol.updater.Updater.UpdateType;
import java.util.logging.Logger;
import org.bukkit.Server;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.updater.Updater.UpdateType;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author dmulloy2
*/
public class UpdaterTest {
private static final int BUKKIT_DEV_ID = 45564;
private static Plugin plugin;
// @BeforeClass
@BeforeAll
public static void preparePlugin() {
Server server = mock(Server.class);
when(server.getUpdateFolder()).thenReturn(null);
plugin = mock(Plugin.class);
String version = System.getProperty("projectVersion");
if (version == null) version = "4.4.0";
if (version == null) {
version = "4.4.0";
}
when(plugin.getDescription()).thenReturn(new PluginDescriptionFile("ProtocolLib", version, null));
when(plugin.getLogger()).thenReturn(Logger.getLogger("ProtocolLib"));
when(plugin.getDataFolder()).thenReturn(null);
when(plugin.getServer()).thenReturn(server);
}
// @Test
@Test
public void testUpdaterType() {
assertEquals(Updater.create(plugin, BUKKIT_DEV_ID, null, UpdateType.NO_DOWNLOAD, true).getClass(), SpigotUpdater.class);
assertEquals(Updater.create(plugin, BUKKIT_DEV_ID, null, UpdateType.NO_DOWNLOAD, true).getClass(),
SpigotUpdater.class);
}
// @Test
@@ -54,23 +55,10 @@ public class UpdaterTest {
try {
remote = updater.getSpigotVersion();
} catch (Throwable ex) {
ex.printStackTrace();
fail("Failed to check for updates");
fail("Failed to check for updates", ex);
}
System.out.println("Determined remote Spigot version: " + remote);
System.out.println("Update available: " + updater.versionCheck(remote));
}
// @Test
public void testBukkitUpdater() {
BukkitUpdater updater = new BukkitUpdater(plugin, BUKKIT_DEV_ID, null, UpdateType.NO_DOWNLOAD, true);
if (! updater.read()) {
fail("Failed to check for updates");
}
String remote = updater.getLatestName();
System.out.println("Determined remote Bukkit Dev version: " + remote);
System.out.println("Update available: " + updater.versionCheck(remote));
}
}
@@ -1,19 +1,17 @@
package com.comphenix.protocol.utility;
import static org.junit.Assert.assertNotNull;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import com.comphenix.protocol.BukkitInitialization;
import java.lang.reflect.Field;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class MinecraftMethodsTest {
@BeforeClass
@BeforeAll
public static void initializeReflection() {
BukkitInitialization.initializePackage();
BukkitInitialization.initializeAll();
}
@Test
@@ -30,10 +28,11 @@ public class MinecraftMethodsTest {
@Test
public void initializePacket() throws NoSuchFieldException, IllegalAccessException {
setNull("packetReadByteBuf");
setNull("packetWriteByteBuf");
this.setNull("packetReadByteBuf");
this.setNull("packetWriteByteBuf");
assertNotNull(MinecraftMethods.getPacketWriteByteBufMethod());
assertNotNull(MinecraftMethods.getPacketReadByteBufMethod());
// TODO it's now a constructor
// assertNotNull(MinecraftMethods.getPacketReadByteBufMethod());
}
}
@@ -1,57 +1,42 @@
package com.comphenix.protocol.utility;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import net.minecraft.server.v1_16_R3.ChatComponentText;
import net.minecraft.server.v1_16_R3.ChunkCoordIntPair;
import net.minecraft.server.v1_16_R3.DataWatcher;
import net.minecraft.server.v1_16_R3.IBlockData;
import net.minecraft.server.v1_16_R3.IChatBaseComponent;
import net.minecraft.server.v1_16_R3.IChatBaseComponent.ChatSerializer;
import net.minecraft.server.v1_16_R3.NBTCompressedStreamTools;
import net.minecraft.server.v1_16_R3.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.server.v1_16_R3.PlayerConnection;
import net.minecraft.server.v1_16_R3.ServerPing;
import net.minecraft.server.v1_16_R3.ServerPing.ServerData;
import net.minecraft.server.v1_16_R3.ServerPing.ServerPingPlayerSample;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_16_R3.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;
import net.minecraft.nbt.NBTCompressedStreamTools;
import net.minecraft.network.chat.ChatComponentText;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes;
import net.minecraft.network.protocol.status.ServerPing;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.server.network.PlayerConnection;
import net.minecraft.world.level.ChunkCoordIntPair;
import net.minecraft.world.level.block.state.IBlockData;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
public class MinecraftReflectionTest {
@BeforeClass
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeItemMeta();
BukkitInitialization.initializeAll();
}
// Mocking objects
private interface FakeEntity {
Entity getBukkitEntity();
}
private interface FakeBlock {
Block getBukkitEntity();
}
@AfterClass
@AfterAll
public static void undoMocking() {
// NOP
MinecraftReflection.minecraftPackage = null;
@@ -69,9 +54,9 @@ public class MinecraftReflectionTest {
verify(block, times(1)).getBukkitEntity();
}
@Test(expected = IllegalArgumentException.class)
@Test
public void testIllegalClass() {
MinecraftReflection.getBukkitEntity("Hello");
assertThrows(IllegalArgumentException.class, () -> MinecraftReflection.getBukkitEntity("Hello"));
}
@Test
@@ -81,7 +66,8 @@ public class MinecraftReflectionTest {
@Test
public void testAttributeSnapshot() {
assertEquals(AttributeSnapshot.class, MinecraftReflection.getAttributeSnapshotClass());
assertEquals(PacketPlayOutUpdateAttributes.AttributeSnapshot.class,
MinecraftReflection.getAttributeSnapshotClass());
}
@Test
@@ -96,7 +82,7 @@ public class MinecraftReflectionTest {
@Test
public void testChatSerializer() {
assertEquals(ChatSerializer.class, MinecraftReflection.getChatSerializerClass());
assertEquals(IChatBaseComponent.ChatSerializer.class, MinecraftReflection.getChatSerializerClass());
}
@Test
@@ -121,12 +107,12 @@ public class MinecraftReflectionTest {
@Test
public void testServerPingPlayerSample() {
assertEquals(ServerPingPlayerSample.class, MinecraftReflection.getServerPingPlayerSampleClass());
assertEquals(ServerPing.ServerPingPlayerSample.class, MinecraftReflection.getServerPingPlayerSampleClass());
}
@Test
public void testServerPingServerData() {
assertEquals(ServerData.class, MinecraftReflection.getServerPingServerDataClass());
assertEquals(ServerPing.ServerData.class, MinecraftReflection.getServerPingServerDataClass());
}
@Test
@@ -153,4 +139,21 @@ public class MinecraftReflectionTest {
public void testGameProfile() {
assertEquals(GameProfile.class, MinecraftReflection.getGameProfileClass());
}
@Test
public void testEnumEntityUseAction() {
// this class is package-private in PacketPlayInUseEntity, so we can only check if no exception is thrown during retrieval
MinecraftReflection.getEnumEntityUseActionClass();
}
// Mocking objects
private interface FakeEntity {
Entity getBukkitEntity();
}
private interface FakeBlock {
Block getBukkitEntity();
}
}
@@ -1,37 +1,38 @@
package com.comphenix.protocol.utility;
import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import org.junit.Test;
import org.junit.jupiter.api.Test;
public class SnapshotVersionTest {
@Test
public void testDates() {
SnapshotVersion a = new SnapshotVersion("12w50b");
SnapshotVersion b = new SnapshotVersion("13w05a");
expect(a.getSnapshotDate(), 12, 50);
expect(b.getSnapshotDate(), 13, 5);
this.expect(a.getSnapshotDate(), 12, 50);
this.expect(b.getSnapshotDate(), 13, 5);
// Test equality
assertEquals(a, new SnapshotVersion("12w50b"));
}
@Test(expected=IllegalArgumentException.class)
@Test
public void testDateParsingProblem() {
// This date is not valid
new SnapshotVersion("12w80a");
assertThrows(IllegalArgumentException.class, () -> new SnapshotVersion("12w80a"));
}
@Test(expected=IllegalArgumentException.class)
@Test
public void testMissingWeekVersion() {
new SnapshotVersion("13w05");
assertThrows(IllegalArgumentException.class, () -> new SnapshotVersion("13w05"));
}
private void expect(Date date, int year, int week) {
Calendar calendar = Calendar.getInstance(Locale.US);
calendar.setTime(date);
@@ -1,38 +1,30 @@
package com.comphenix.protocol.utility;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.*;
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.*" })
//@PrepareForTest(CraftItemFactory.class)
public class StreamSerializerTest {
@BeforeClass
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeItemMeta();
BukkitInitialization.initializeAll();
}
/*@Test
public void testMinecraftReflection() {
assertEquals(IntHashMap.class, MinecraftReflection.getIntHashMapClass());
}*/
@Test
public void testStrings() throws IOException {
StreamSerializer serializer = new StreamSerializer();
@@ -1,13 +1,16 @@
package com.comphenix.protocol.utility;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.reflect.accessors.Accessors;
import java.lang.reflect.Field;
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) {
@@ -42,4 +45,8 @@ public class TestUtils {
return first.getType().equals(second.getType());
}
}
public static void setFinalField(Object obj, Field field, Object newValue) {
Accessors.getFieldAccessor(field, true).set(obj, newValue);
}
}
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff. Show more