Compare commits
92
Commits
4.7.0
...
update/1193
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9c623a254 | ||
|
|
fe0abbf9b3 | ||
|
|
92b604506f | ||
|
|
574c34ad5c | ||
|
|
115cc83040 | ||
|
|
a18c418f42 | ||
|
|
6aaf0ec26b | ||
|
|
86e586da26 | ||
|
|
069783a353 | ||
|
|
cc95e19ba4 | ||
|
|
5ef24a7471 | ||
|
|
475f89ab89 | ||
|
|
e3d4339659 | ||
|
|
e7d54cdabd | ||
|
|
c31133c20f | ||
|
|
69ae3656b5 | ||
|
|
43145bd478 | ||
|
|
b7c1e096c4 | ||
|
|
6707c4811e | ||
|
|
2092b8f48e | ||
|
|
20e73369fa | ||
|
|
abc0db8281 | ||
|
|
1beb95115f | ||
|
|
7fcfcdc365 | ||
|
|
2be216899a | ||
|
|
8876ce323b | ||
|
|
575174580e | ||
|
|
7fd4ec3172 | ||
|
|
a75d383001 | ||
|
|
7ddfd4f347 | ||
|
|
7e137cbfc5 | ||
|
|
d40762e69d | ||
|
|
ae19478007 | ||
|
|
11a8184c3e | ||
|
|
c3dc00de05 | ||
|
|
84cb541866 | ||
|
|
624f6aaca6 | ||
|
|
c5f0550953 | ||
|
|
7f0bc7fd24 | ||
|
|
96155b1065 | ||
|
|
23dac3287b | ||
|
|
0bbbd961aa | ||
|
|
dd19e1040a | ||
|
|
e45c16d490 | ||
|
|
a19959cea4 | ||
|
|
4f18d37832 | ||
|
|
a2bf242097 | ||
|
|
4e105c59ed | ||
|
|
59ca841ed5 | ||
|
|
5e8f044a18 | ||
|
|
4db1e39ac7 | ||
|
|
e202503c09 | ||
|
|
aed98abac6 | ||
|
|
250f94e9cd | ||
|
|
d7bf43001f | ||
|
|
868b357527 | ||
|
|
9a609c2053 | ||
|
|
f3acce99d8 | ||
|
|
374e6cd5ee | ||
|
|
84a0b5ffdd | ||
|
|
4cc3957723 | ||
|
|
a0a5469988 | ||
|
|
240920d642 | ||
|
|
764195bd55 | ||
|
|
7bfee67a29 | ||
|
|
c87604cf0c | ||
|
|
4096952c16 | ||
|
|
b4eff32213 | ||
|
|
d361526371 | ||
|
|
0d4e4c818f | ||
|
|
073bfa2b86 | ||
|
|
f0059f39f6 | ||
|
|
41bb4bacb2 | ||
|
|
9487c42985 | ||
|
|
55f7b67f9d | ||
|
|
e44d1e6051 | ||
|
|
baecaf4ca4 | ||
|
|
151d4a289f | ||
|
|
1b4d79b302 | ||
|
|
74833f8680 | ||
|
|
e77f8ced4c | ||
|
|
1cd83e493d | ||
|
|
40b6c66491 | ||
|
|
723fc2c7ec | ||
|
|
dd687ce175 | ||
|
|
8774d87d59 | ||
|
|
8361cf078f | ||
|
|
dbedab0c14 | ||
|
|
5dda8c8ab1 | ||
|
|
4c0c18d7c6 | ||
|
|
9c20455bf6 | ||
|
|
8175443da0 |
No files matched your search
@@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
||||
@@ -2,6 +2,7 @@ _Follow this template except for feature requests. Use pastebin when providing /
|
||||
|
||||
Make sure you've done the following:
|
||||
- [ ] You're using the latest build for your server version
|
||||
- [ ] This isn't fixed in a recent development build
|
||||
- [ ] This isn't an issue caused by another plugin
|
||||
- [ ] You've checked for duplicate issues
|
||||
- [ ] You didn't use `/reload`
|
||||
|
||||
@@ -7,6 +7,8 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
- [ ] This issue is not solved in a development build
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
- [ ] This feature is not currently present in a development build
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
|
||||
@@ -8,13 +8,15 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '16'
|
||||
check-latest: true
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
cache: 'maven'
|
||||
|
||||
- name: Run maven build lifecycle
|
||||
run: mvn --batch-mode clean test package
|
||||
@@ -22,5 +24,5 @@ jobs:
|
||||
- name: Upload plugin file
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ProtocolLib
|
||||
path: target/ProtocolLib.jar
|
||||
name: ProtocolLib
|
||||
path: target/ProtocolLib.jar
|
||||
@@ -0,0 +1,41 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: '28 9 * * 1'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
cache: 'maven'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: 'java'
|
||||
|
||||
- name: Run maven build lifecycle
|
||||
run: mvn --batch-mode clean test package
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
+2
-1
@@ -179,4 +179,5 @@ logs/
|
||||
|
||||
.gradle/
|
||||
build/
|
||||
logs/
|
||||
logs/
|
||||
.java-version
|
||||
@@ -1,169 +1,167 @@
|
||||
# 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
|
||||
and outgoing [packets](https://www.wiki.vg/Protocol), or inject custom packets into the
|
||||
stream. This is quite cumbersome to do, however, and most implementations will break
|
||||
as soon as a new version of Minecraft has been released, mostly due to obfuscation.
|
||||
|
||||
Critically, different plugins that use this approach may _hook_ into the same classes,
|
||||
with unpredictable outcomes. More than often this causes plugins to crash, but it may also
|
||||
lead to more subtle bugs.
|
||||
|
||||
Currently maintained by dmulloy2 on behalf of [Spigot](https://www.spigotmc.org/).
|
||||
|
||||
### Resources
|
||||
|
||||
* [Resource Page](https://www.spigotmc.org/resources/protocollib.1997/)
|
||||
* [Dev Builds](https://ci.dmulloy2.net/job/ProtocolLib)
|
||||
* [JavaDoc](https://ci.dmulloy2.net/job/ProtocolLib/javadoc)
|
||||
|
||||
### Compilation
|
||||
|
||||
ProtocolLib is built with Maven and requires Spigot and SpigotAPI, which can be found [here](https://www.spigotmc.org/wiki/buildtools/).
|
||||
|
||||
### A new API
|
||||
|
||||
__ProtocolLib__ attempts to solve this problem by providing a event API, much like Bukkit,
|
||||
that allows plugins to monitor, modify, or cancel packets sent and received. But, more importantly,
|
||||
the API also hides all the gritty, obfuscated classes with a simple index based read/write system.
|
||||
You no longer have to reference CraftBukkit!
|
||||
|
||||
### Using ProtocolLib
|
||||
|
||||
To use this library, first add ProtocolLib.jar to your Java build path. Then, add ProtocolLib
|
||||
as a dependency or soft dependency to your plugin.yml file like any other plugin:
|
||||
|
||||
````yml
|
||||
depend: [ProtocolLib]
|
||||
````
|
||||
|
||||
You can also add ProtocolLib as a Maven dependency:
|
||||
|
||||
````xml
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||
</repository>
|
||||
...
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>4.6.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
````
|
||||
|
||||
Or use the maven dependency with gradle:
|
||||
|
||||
```gradle
|
||||
repositories {
|
||||
maven { url "https://repo.dmulloy2.net/repository/public/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.6.0";
|
||||
}
|
||||
```
|
||||
|
||||
Then get a reference to ProtocolManager in onLoad() or onEnable() and you're good to go.
|
||||
|
||||
````java
|
||||
private ProtocolManager protocolManager;
|
||||
|
||||
public void onLoad() {
|
||||
protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
}
|
||||
````
|
||||
|
||||
To listen for packets sent by the server to a client, add a server-side listener:
|
||||
|
||||
````java
|
||||
// Disable all sound effects
|
||||
protocolManager.addPacketListener(
|
||||
new PacketAdapter(this, ListenerPriority.NORMAL,
|
||||
PacketType.Play.Server.NAMED_SOUND_EFFECT) {
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
// Item packets (id: 0x29)
|
||||
if (event.getPacketType() ==
|
||||
PacketType.Play.Server.NAMED_SOUND_EFFECT) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
````
|
||||
|
||||
It's also possible to read and modify the content of these packets. For instance, you can create a global
|
||||
censor by listening for Packet3Chat events:
|
||||
|
||||
````java
|
||||
// Censor
|
||||
protocolManager.addPacketListener(new PacketAdapter(this,
|
||||
ListenerPriority.NORMAL,
|
||||
PacketType.Play.Client.CHAT) {
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CHAT) {
|
||||
PacketContainer packet = event.getPacket();
|
||||
String message = packet.getStrings().read(0);
|
||||
|
||||
if (message.contains("shit")
|
||||
|| message.contains("damn")) {
|
||||
event.setCancelled(true);
|
||||
event.getPlayer().sendMessage("Bad manners!");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
````
|
||||
|
||||
### Sending packets
|
||||
|
||||
Normally, you might have to do something ugly like the following:
|
||||
|
||||
````java
|
||||
Packet60Explosion fakeExplosion = new Packet60Explosion();
|
||||
|
||||
fakeExplosion.a = player.getLocation().getX();
|
||||
fakeExplosion.b = player.getLocation().getY();
|
||||
fakeExplosion.c = player.getLocation().getZ();
|
||||
fakeExplosion.d = 3.0F;
|
||||
fakeExplosion.e = new ArrayList<Object>();
|
||||
|
||||
((CraftPlayer) player).getHandle().netServerHandler.sendPacket(fakeExplosion);
|
||||
````
|
||||
|
||||
But with ProtocolLib, you can turn that into something more manageable. Notice that
|
||||
you don't have to create an ArrayList with this version:
|
||||
|
||||
````java
|
||||
|
||||
PacketContainer fakeExplosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
|
||||
fakeExplosion.getDoubles().
|
||||
write(0, player.getLocation().getX()).
|
||||
write(1, player.getLocation().getY()).
|
||||
write(2, player.getLocation().getZ());
|
||||
fakeExplosion.getFloat().write(0, 3.0F);
|
||||
|
||||
try {
|
||||
protocolManager.sendServerPacket(player, fakeExplosion);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(
|
||||
"Cannot send packet " + fakeExplosion, e);
|
||||
}
|
||||
````
|
||||
|
||||
### Compatibility
|
||||
|
||||
One of the main goals of this project was to achieve maximum compatibility with CraftBukkit. And the end
|
||||
result is quite flexible. Aside from netty package changes, it should be resilient against future changes.
|
||||
It's likely that I won't have to update ProtocolLib for anything but bug fixes and new features.
|
||||
|
||||
How is this possible? It all comes down to reflection in the end. Essentially, no name is hard coded -
|
||||
every field, method and class is deduced by looking at field types, package names or parameter
|
||||
types. It's remarkably consistent across different versions.
|
||||
# ProtocolLib
|
||||
|
||||
Certain tasks are impossible to perform with the standard Bukkit API, and may require
|
||||
working with and even modifying Minecraft directly. A common technique is to modify incoming
|
||||
and outgoing [packets](https://www.wiki.vg/Protocol), or inject custom packets into the
|
||||
stream. This is quite cumbersome to do, however, and most implementations will break
|
||||
as soon as a new version of Minecraft has been released, mostly due to obfuscation.
|
||||
|
||||
Critically, different plugins that use this approach may _hook_ into the same classes,
|
||||
with unpredictable outcomes. More than often this causes plugins to crash, but it may also
|
||||
lead to more subtle bugs.
|
||||
|
||||
Currently maintained by dmulloy2 on behalf of [Spigot](https://www.spigotmc.org/).
|
||||
|
||||
### Resources
|
||||
|
||||
* [Resource Page](https://www.spigotmc.org/resources/protocollib.1997/)
|
||||
* [Dev Builds](https://ci.dmulloy2.net/job/ProtocolLib)
|
||||
* [JavaDoc](https://ci.dmulloy2.net/job/ProtocolLib/javadoc/index.html)
|
||||
|
||||
### Compilation
|
||||
|
||||
ProtocolLib is built with [Maven](https://maven.apache.org/). If you have it installed, just run
|
||||
`mvn package` in the root project folder.
|
||||
|
||||
### A new API
|
||||
|
||||
__ProtocolLib__ attempts to solve this problem by providing an event API, much like Bukkit,
|
||||
that allows plugins to monitor, modify, or cancel packets sent and received. But, more importantly,
|
||||
the API also hides all the gritty, obfuscated classes with a simple index based read/write system.
|
||||
You no longer have to reference CraftBukkit!
|
||||
|
||||
### Using ProtocolLib
|
||||
|
||||
To use this library, first add ProtocolLib.jar to your Java build path. Then, add ProtocolLib
|
||||
as a dependency or soft dependency to your plugin.yml file like any other plugin:
|
||||
|
||||
````yml
|
||||
depend: [ ProtocolLib ]
|
||||
````
|
||||
|
||||
You can also add ProtocolLib as a Maven dependency:
|
||||
|
||||
````xml
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>4.7.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
````
|
||||
|
||||
Or use the maven dependency with gradle:
|
||||
|
||||
```gradle
|
||||
repositories {
|
||||
maven { url "https://repo.dmulloy2.net/repository/public/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.7.0";
|
||||
}
|
||||
```
|
||||
|
||||
Then get a reference to ProtocolManager in onLoad() or onEnable() and you're good to go.
|
||||
|
||||
````java
|
||||
private ProtocolManager protocolManager;
|
||||
|
||||
public void onLoad() {
|
||||
protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
}
|
||||
````
|
||||
|
||||
To listen for packets sent by the server to a client, add a server-side listener:
|
||||
|
||||
````java
|
||||
// Disable all sound effects
|
||||
protocolManager.addPacketListener(new PacketAdapter(
|
||||
this,
|
||||
ListenerPriority.NORMAL,
|
||||
PacketType.Play.Server.NAMED_SOUND_EFFECT
|
||||
) {
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
````
|
||||
|
||||
It's also possible to read and modify the content of these packets. For instance, you can create a global
|
||||
censor by listening for Packet3Chat events:
|
||||
|
||||
````java
|
||||
// Censor
|
||||
protocolManager.addPacketListener(new PacketAdapter(
|
||||
this,
|
||||
ListenerPriority.NORMAL,
|
||||
PacketType.Play.Client.CHAT
|
||||
) {
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
PacketContainer packet = event.getPacket();
|
||||
String message = packet.getStrings().read(0);
|
||||
|
||||
if (message.contains("shit") || message.contains("damn")) {
|
||||
event.setCancelled(true);
|
||||
event.getPlayer().sendMessage("Bad manners!");
|
||||
}
|
||||
}
|
||||
});
|
||||
````
|
||||
|
||||
### Sending packets
|
||||
|
||||
Normally, you might have to do something ugly like the following:
|
||||
|
||||
````java
|
||||
PacketPlayOutExplosion fakeExplosion = new PacketPlayOutExplosion(
|
||||
player.getLocation().getX(),
|
||||
player.getLocation().getY(),
|
||||
player.getLocation().getZ(),
|
||||
3.0F,
|
||||
new ArrayList<>(),
|
||||
new Vec3D(
|
||||
player.getVelocity().getX() + 1,
|
||||
player.getVelocity().getY() + 1,
|
||||
player.getVelocity().getZ() + 1
|
||||
)
|
||||
);
|
||||
|
||||
((CraftPlayer) player).getHandle().b.a(fakeExplosion);
|
||||
````
|
||||
|
||||
But with ProtocolLib, you can turn that into something more manageable:
|
||||
|
||||
````java
|
||||
PacketContainer fakeExplosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
|
||||
fakeExplosion.getDoubles()
|
||||
.write(0, player.getLocation().getX())
|
||||
.write(1, player.getLocation().getY())
|
||||
.write(2, player.getLocation().getZ());
|
||||
fakeExplosion.getFloat().write(0, 3.0F);
|
||||
fakeExplosion.getBlockPositionCollectionModifier().write(0, new ArrayList<>());
|
||||
fakeExplosion.getVectors().write(0, player.getVelocity().add(new Vector(1, 1, 1)));
|
||||
|
||||
protocolManager.sendServerPacket(player, fakeExplosion);
|
||||
````
|
||||
|
||||
### Compatibility
|
||||
|
||||
One of the main goals of this project was to achieve maximum compatibility with CraftBukkit. And the end
|
||||
result is quite flexible. It's likely that I won't have to update ProtocolLib for anything but bug fixes
|
||||
and new features.
|
||||
|
||||
How is this possible? It all comes down to reflection in the end. Essentially, no name is hard coded -
|
||||
every field, method and class is deduced by looking at field types, package names or parameter
|
||||
types. It's remarkably consistent across different versions.
|
||||
@@ -25,7 +25,7 @@
|
||||
</repository>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>http://repo.dmulloy2.net/content/groups/public/</url>
|
||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public abstract class TinyProtocol {
|
||||
private List<Object> networkManagers;
|
||||
|
||||
// Injected channel handlers
|
||||
private List<Channel> serverChannels = Lists.newArrayList();
|
||||
private List<Channel> serverChannels = new ArrayList<>();
|
||||
private ChannelInboundHandlerAdapter serverChannelHandler;
|
||||
private ChannelInitializer<Channel> beginInitProtocol;
|
||||
private ChannelInitializer<Channel> endInitProtocol;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
before_install:
|
||||
- source "$HOME/.sdkman/bin/sdkman-init.sh"
|
||||
- sdk update
|
||||
- sdk install java 16.0.1.hs-adpt
|
||||
- sdk use java 16.0.1.hs-adpt
|
||||
- sdk install java 17.0.4-tem
|
||||
- sdk use java 17.0.4-tem
|
||||
@@ -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.7.0-SNAPSHOT</version>
|
||||
<version>5.0.0-SNAPSHOT</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.17.1-R0.1-SNAPSHOT</spigot.version>
|
||||
<junit.version>5.9.0</junit.version>
|
||||
<mockito.version>4.6.1</mockito.version>
|
||||
<netty.version>4.1.77.Final</netty.version>
|
||||
<spigot.version>1.19.3-R0.1-SNAPSHOT</spigot.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@@ -38,7 +40,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<version>3.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@@ -71,7 +73,7 @@
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<version>3.10.1</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>
|
||||
@@ -98,7 +100,15 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M5</version>
|
||||
<version>3.0.0-M7</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,22 +118,22 @@
|
||||
<value>${project.version}</value>
|
||||
</property>
|
||||
</systemProperties>
|
||||
<!-- TODO figure out a better way to do this before Java 17 -->
|
||||
<!-- We're currently waiting on powermock, but we may need to switch -->
|
||||
<argLine>--illegal-access=permit</argLine>
|
||||
<!-- 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.4.0</version>
|
||||
<configuration>
|
||||
<failOnError>false</failOnError>
|
||||
<encoding>ISO-8859-1</encoding>
|
||||
<doctitle>ProtocolLib JavaDocs</doctitle>
|
||||
<windowtitle>ProtocolLib JavaDocs</windowtitle>
|
||||
<bottom>Copyright © {inceptionYear}–{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>
|
||||
@@ -185,7 +195,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<version>3.4.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
@@ -227,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>
|
||||
@@ -236,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>
|
||||
@@ -246,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>
|
||||
@@ -277,61 +287,92 @@
|
||||
</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>
|
||||
<exclusion>
|
||||
<groupId>io.netty</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.5.1</version>
|
||||
<version>4.11.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy -->
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.12</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>
|
||||
|
||||
@@ -41,9 +41,9 @@ abstract class CommandBase implements CommandExecutor {
|
||||
|
||||
public static final String PERMISSION_ADMIN = "protocol.admin";
|
||||
|
||||
private String permission;
|
||||
private String name;
|
||||
private int minimumArgumentCount;
|
||||
private final String permission;
|
||||
private final String name;
|
||||
private final int minimumArgumentCount;
|
||||
|
||||
protected ErrorReporter reporter;
|
||||
|
||||
@@ -97,18 +97,23 @@ abstract class CommandBase implements CommandExecutor {
|
||||
Boolean result = null;
|
||||
|
||||
if (!arguments.isEmpty()) {
|
||||
String arg = arguments.peek();
|
||||
|
||||
if (arg.equalsIgnoreCase("true") || arg.equalsIgnoreCase("on"))
|
||||
result = true;
|
||||
else if (arg.equalsIgnoreCase(parameterName))
|
||||
result = true;
|
||||
else if (arg.equalsIgnoreCase("false") || arg.equalsIgnoreCase("off"))
|
||||
result = false;
|
||||
final String arg = arguments.peek().toLowerCase();
|
||||
switch (arg) {
|
||||
case "true":
|
||||
case "on":
|
||||
result = true;
|
||||
break;
|
||||
case "false":
|
||||
case "off":
|
||||
result = false;
|
||||
break;
|
||||
default:
|
||||
if (arg.equalsIgnoreCase(parameterName)) result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
arguments.poll();
|
||||
if (result != null) arguments.poll();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -119,7 +124,7 @@ abstract class CommandBase implements CommandExecutor {
|
||||
* @return A queue that contains every element in the array, starting at the given index.
|
||||
*/
|
||||
protected Deque<String> toQueue(String[] args, int start) {
|
||||
return new ArrayDeque<String>(Arrays.asList(args).subList(start, args.length));
|
||||
return new ArrayDeque<>(Arrays.asList(args).subList(start, args.length));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.comphenix.protocol;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -26,7 +27,6 @@ import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* A command to apply JavaScript filtering to the packet command.
|
||||
@@ -79,7 +79,7 @@ public class CommandFilter extends CommandBase {
|
||||
public Filter(String name, String predicate, Set<PacketType> packets) {
|
||||
this.name = name;
|
||||
this.predicate = predicate;
|
||||
this.packets = Sets.newHashSet(packets);
|
||||
this.packets = new HashSet<>(packets);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +103,7 @@ public class CommandFilter extends CommandBase {
|
||||
* @return Set of packets this filter applies to.
|
||||
*/
|
||||
public Set<PacketType> getRanges() {
|
||||
return Sets.newHashSet(packets);
|
||||
return new HashSet<>(packets);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -34,25 +35,17 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.utility.ByteBuddyGenerated;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.PrettyPrinter;
|
||||
import com.comphenix.protocol.reflect.PrettyPrinter.ObjectPrinter;
|
||||
import com.comphenix.protocol.utility.ChatExtensions;
|
||||
import com.comphenix.protocol.utility.HexDumper;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Handles the "packet" debug command.
|
||||
@@ -121,13 +114,7 @@ class CommandPacket extends CommandBase {
|
||||
* @return TRUE if the message was sent successfully, FALSE otherwise.
|
||||
*/
|
||||
public void sendMessageSilently(CommandSender receiver, String message) {
|
||||
try {
|
||||
chatter.sendMessageSilently(receiver, message);
|
||||
} catch (InvocationTargetException e) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_SEND_MESSAGE).error(e).callerParam(receiver, message)
|
||||
);
|
||||
}
|
||||
chatter.sendMessageSilently(receiver, message);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,13 +123,7 @@ class CommandPacket extends CommandBase {
|
||||
* @param permission - permission required to receieve the message. NULL to target everyone.
|
||||
*/
|
||||
public void broadcastMessageSilently(String message, String permission) {
|
||||
try {
|
||||
chatter.broadcastMessageSilently(message, permission);
|
||||
} catch (InvocationTargetException e) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_SEND_MESSAGE).error(e).callerParam(message, permission)
|
||||
);
|
||||
}
|
||||
chatter.broadcastMessageSilently(message, permission);
|
||||
}
|
||||
|
||||
private void printPage(CommandSender sender, int pageIndex) {
|
||||
@@ -306,7 +287,7 @@ class CommandPacket extends CommandBase {
|
||||
}
|
||||
|
||||
private Set<PacketType> filterTypes(Set<PacketType> types, Sender sender) {
|
||||
Set<PacketType> result = Sets.newHashSet();
|
||||
final Set<PacketType> result = new HashSet<>();
|
||||
|
||||
for (PacketType type : types) {
|
||||
if (type.getSender() == sender) {
|
||||
@@ -461,19 +442,21 @@ class CommandPacket extends CommandBase {
|
||||
return listener;
|
||||
}
|
||||
|
||||
private SubCommand parseCommand(Deque<String> arguments) {
|
||||
String text = arguments.poll().toLowerCase();
|
||||
|
||||
// Parse this too
|
||||
if ("add".startsWith(text))
|
||||
return SubCommand.ADD;
|
||||
else if ("remove".startsWith(text))
|
||||
return SubCommand.REMOVE;
|
||||
else if ("names".startsWith(text))
|
||||
return SubCommand.NAMES;
|
||||
else if ("page".startsWith(text))
|
||||
return SubCommand.PAGE;
|
||||
else
|
||||
throw new IllegalArgumentException(text + " is not a valid sub command. Must be add or remove.");
|
||||
private SubCommand parseCommand(Deque<String> arguments)
|
||||
{
|
||||
final String text = arguments.remove().toLowerCase();
|
||||
|
||||
switch (arguments.remove().toLowerCase()) {
|
||||
case "add":
|
||||
return SubCommand.ADD;
|
||||
case "remove":
|
||||
return SubCommand.REMOVE;
|
||||
case "names":
|
||||
return SubCommand.NAMES;
|
||||
case "page":
|
||||
return SubCommand.PAGE;
|
||||
default:
|
||||
throw new IllegalArgumentException(text + " is not a valid sub command. Must be add or remove.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,14 +218,13 @@ class CommandProtocol extends CommandBase {
|
||||
private static SimpleDateFormat TIMESTAMP_FORMAT;
|
||||
|
||||
private void dump(CommandSender sender) {
|
||||
Closer closer = Closer.create();
|
||||
|
||||
if (FILE_FORMAT == null)
|
||||
FILE_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss");
|
||||
if (TIMESTAMP_FORMAT == null)
|
||||
TIMESTAMP_FORMAT = new SimpleDateFormat("MM/dd/yy HH:mm:ss");
|
||||
|
||||
try {
|
||||
try (Closer closer = Closer.create()) {
|
||||
Date date = new Date();
|
||||
File file = new File(plugin.getDataFolder(), "dump-" + FILE_FORMAT.format(date) + ".txt");
|
||||
if (file.exists()) {
|
||||
@@ -233,7 +232,7 @@ class CommandProtocol extends CommandBase {
|
||||
}
|
||||
|
||||
file.createNewFile();
|
||||
|
||||
|
||||
FileWriter fw = closer.register(new FileWriter(file));
|
||||
PrintWriter pw = closer.register(new PrintWriter(fw));
|
||||
|
||||
@@ -285,8 +284,6 @@ class CommandProtocol extends CommandBase {
|
||||
} catch (IOException ex) {
|
||||
ProtocolLogger.log(Level.SEVERE, "Failed to create dump:", ex);
|
||||
sender.sendMessage(ChatColor.RED + "Failed to create dump! Check console!");
|
||||
} finally {
|
||||
closer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,268 +1,284 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2017 Dan Mulloy
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.*;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.netty.WirePacket;
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import org.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;
|
||||
|
||||
/**
|
||||
* Logs packets to a given stream
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class PacketLogging implements CommandExecutor, PacketListener {
|
||||
public static final String NAME = "packetlog";
|
||||
|
||||
private List<PacketType> sendingTypes = new ArrayList<>();
|
||||
private List<PacketType> receivingTypes = new ArrayList<>();
|
||||
|
||||
private ListeningWhitelist sendingWhitelist;
|
||||
private ListeningWhitelist receivingWhitelist;
|
||||
|
||||
private Logger fileLogger;
|
||||
private LogLocation location = LogLocation.FILE;
|
||||
|
||||
private final ProtocolManager manager;
|
||||
private final Plugin plugin;
|
||||
|
||||
PacketLogging(Plugin plugin, ProtocolManager manager) {
|
||||
this.plugin = plugin;
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
PacketType type = null;
|
||||
|
||||
try {
|
||||
if (args.length > 2) {
|
||||
Protocol protocol;
|
||||
|
||||
try {
|
||||
protocol = Protocol.valueOf(args[0].toUpperCase());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Unknown protocol " + args[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Sender pSender;
|
||||
|
||||
try {
|
||||
pSender = Sender.valueOf(args[1].toUpperCase());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Unknown sender: " + args[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
try { // Try IDs first
|
||||
int id = Integer.parseInt(args[2]);
|
||||
type = PacketType.findCurrent(protocol, pSender, id);
|
||||
} catch (NumberFormatException ex) { // Check packet names
|
||||
String name = args[2];
|
||||
for (PacketType packet : PacketType.values()) {
|
||||
if (packet.getProtocol() == protocol && packet.getSender() == pSender) {
|
||||
if (packet.name().equalsIgnoreCase(name)) {
|
||||
type = packet;
|
||||
break;
|
||||
}
|
||||
for (String className : packet.getClassNames()) {
|
||||
if (className.equalsIgnoreCase(name)) {
|
||||
type = packet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException ex) { // RIP
|
||||
type = null;
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Unknown packet: " + args[2]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 3) {
|
||||
if (args[3].equalsIgnoreCase("console")) {
|
||||
this.location = LogLocation.CONSOLE;
|
||||
} else {
|
||||
this.location = LogLocation.FILE;
|
||||
}
|
||||
}
|
||||
|
||||
if (pSender == Sender.CLIENT) {
|
||||
if (receivingTypes.contains(type)) {
|
||||
receivingTypes.remove(type);
|
||||
} else {
|
||||
receivingTypes.add(type);
|
||||
}
|
||||
} else {
|
||||
if (sendingTypes.contains(type)) {
|
||||
sendingTypes.remove(type);
|
||||
} else {
|
||||
sendingTypes.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
startLogging();
|
||||
sender.sendMessage(ChatColor.GREEN + "Now logging " + type.getPacketClass().getSimpleName());
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.RED + "Invalid syntax: /packetlog <protocol> <sender> <packet> [location]");
|
||||
return true;
|
||||
} catch (Throwable ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Failed to parse command: " + ex.toString());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void startLogging() {
|
||||
manager.removePacketListener(this);
|
||||
|
||||
if (sendingTypes.isEmpty() && receivingTypes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendingWhitelist = ListeningWhitelist.newBuilder().types(sendingTypes).build();
|
||||
this.receivingWhitelist = ListeningWhitelist.newBuilder().types(receivingTypes).build();
|
||||
|
||||
// Setup the file logger if it hasn't been already
|
||||
if (location == LogLocation.FILE && fileLogger == null) {
|
||||
fileLogger = Logger.getLogger("ProtocolLib-FileLogging");
|
||||
|
||||
for (Handler handler : fileLogger.getHandlers())
|
||||
fileLogger.removeHandler(handler);
|
||||
fileLogger.setUseParentHandlers(false);
|
||||
|
||||
try {
|
||||
File logFile = new File(plugin.getDataFolder(), "log.log");
|
||||
FileHandler handler = new FileHandler(logFile.getAbsolutePath(), true);
|
||||
handler.setFormatter(new LogFormatter());
|
||||
fileLogger.addHandler(handler);
|
||||
} catch (IOException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to obtain log file:", ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
manager.addPacketListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
log(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
log(event);
|
||||
}
|
||||
|
||||
// Here's where the magic happens
|
||||
|
||||
private static String hexDump(byte[] bytes) throws IOException {
|
||||
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||
HexDump.dump(bytes, 0, output, 0);
|
||||
return new String(output.toByteArray(), Charsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
private void log(PacketEvent event) {
|
||||
try {
|
||||
byte[] bytes = WirePacket.bytesFromPacket(event.getPacket());
|
||||
String hexDump = hexDump(bytes);
|
||||
|
||||
if (location == LogLocation.FILE) {
|
||||
fileLogger.log(Level.INFO, event.getPacketType() + ":");
|
||||
fileLogger.log(Level.INFO, hexDump);
|
||||
fileLogger.log(Level.INFO, "");
|
||||
} else {
|
||||
System.out.println(event.getPacketType() + ":");
|
||||
System.out.println(hexDump);
|
||||
System.out.println();
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to log packet " + event.getPacketType() + ":", ex);
|
||||
plugin.getLogger().log(Level.WARNING, "Clearing packet logger...");
|
||||
|
||||
sendingTypes.clear();
|
||||
receivingTypes.clear();
|
||||
startLogging();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getSendingWhitelist() {
|
||||
return sendingWhitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getReceivingWhitelist() {
|
||||
return receivingWhitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
private enum LogLocation {
|
||||
CONSOLE, FILE
|
||||
}
|
||||
|
||||
private static class LogFormatter extends Formatter {
|
||||
private static final SimpleDateFormat DATE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
|
||||
private static final String FORMAT = "[{0}] {1}";
|
||||
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
String string = formatMessage(record);
|
||||
if (string.isEmpty()) {
|
||||
return LINE_SEPARATOR;
|
||||
}
|
||||
|
||||
StringBuilder message = new StringBuilder();
|
||||
message.append(MessageFormat.format(FORMAT, DATE.format(record.getMillis()), string));
|
||||
message.append(LINE_SEPARATOR);
|
||||
return message.toString();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2017 Dan Mulloy
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.netty.WirePacket;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Logs packets to a given stream
|
||||
* @author dmulloy2
|
||||
*/
|
||||
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<>();
|
||||
|
||||
private ListeningWhitelist sendingWhitelist;
|
||||
private ListeningWhitelist receivingWhitelist;
|
||||
|
||||
private Logger fileLogger;
|
||||
private LogLocation location = LogLocation.FILE;
|
||||
|
||||
private final ProtocolManager manager;
|
||||
private final Plugin plugin;
|
||||
|
||||
PacketLogging(Plugin plugin, ProtocolManager manager) {
|
||||
this.plugin = plugin;
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
PacketType type = null;
|
||||
|
||||
try {
|
||||
if (args.length > 2) {
|
||||
Protocol protocol;
|
||||
|
||||
try {
|
||||
protocol = Protocol.valueOf(args[0].toUpperCase());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Unknown protocol " + args[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Sender pSender;
|
||||
|
||||
try {
|
||||
pSender = Sender.valueOf(args[1].toUpperCase());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Unknown sender: " + args[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
try { // Try IDs first
|
||||
int id = Integer.parseInt(args[2]);
|
||||
type = PacketType.findCurrent(protocol, pSender, id);
|
||||
} catch (NumberFormatException ex) { // Check packet names
|
||||
String name = args[2];
|
||||
for (PacketType packet : PacketType.values()) {
|
||||
if (packet.getProtocol() == protocol && packet.getSender() == pSender) {
|
||||
if (packet.name().equalsIgnoreCase(name)) {
|
||||
type = packet;
|
||||
break;
|
||||
}
|
||||
for (String className : packet.getClassNames()) {
|
||||
if (className.equalsIgnoreCase(name)) {
|
||||
type = packet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException ex) { // RIP
|
||||
type = null;
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Unknown packet: " + args[2]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 3) {
|
||||
if (args[3].equalsIgnoreCase("console")) {
|
||||
this.location = LogLocation.CONSOLE;
|
||||
} else {
|
||||
this.location = LogLocation.FILE;
|
||||
}
|
||||
}
|
||||
|
||||
if (pSender == Sender.CLIENT) {
|
||||
if (receivingTypes.contains(type)) {
|
||||
receivingTypes.remove(type);
|
||||
} else {
|
||||
receivingTypes.add(type);
|
||||
}
|
||||
} else {
|
||||
if (sendingTypes.contains(type)) {
|
||||
sendingTypes.remove(type);
|
||||
} else {
|
||||
sendingTypes.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
startLogging();
|
||||
sender.sendMessage(ChatColor.GREEN + "Now logging " + type.getPacketClass().getSimpleName());
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.RED + "Invalid syntax: /packetlog <protocol> <sender> <packet> [location]");
|
||||
return true;
|
||||
} catch (Throwable ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Failed to parse command: " + ex.toString());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void startLogging() {
|
||||
manager.removePacketListener(this);
|
||||
|
||||
if (sendingTypes.isEmpty() && receivingTypes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendingWhitelist = ListeningWhitelist.newBuilder().types(sendingTypes).build();
|
||||
this.receivingWhitelist = ListeningWhitelist.newBuilder().types(receivingTypes).build();
|
||||
|
||||
// Setup the file logger if it hasn't been already
|
||||
if (location == LogLocation.FILE && fileLogger == null) {
|
||||
fileLogger = Logger.getLogger("ProtocolLib-FileLogging");
|
||||
|
||||
for (Handler handler : fileLogger.getHandlers())
|
||||
fileLogger.removeHandler(handler);
|
||||
fileLogger.setUseParentHandlers(false);
|
||||
|
||||
try {
|
||||
File logFile = new File(plugin.getDataFolder(), "log.log");
|
||||
FileHandler handler = new FileHandler(logFile.getAbsolutePath(), true);
|
||||
handler.setFormatter(new LogFormatter());
|
||||
fileLogger.addHandler(handler);
|
||||
} catch (IOException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to obtain log file:", ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
manager.addPacketListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
log(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
log(event);
|
||||
}
|
||||
|
||||
// Here's where the magic happens
|
||||
|
||||
private static String hexDump(byte[] bytes) throws IOException {
|
||||
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||
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(), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
private void log(PacketEvent event) {
|
||||
try {
|
||||
byte[] bytes = WirePacket.bytesFromPacket(event.getPacket());
|
||||
String hexDump = hexDump(bytes);
|
||||
|
||||
if (location == LogLocation.FILE) {
|
||||
fileLogger.log(Level.INFO, event.getPacketType() + ":");
|
||||
fileLogger.log(Level.INFO, hexDump);
|
||||
fileLogger.log(Level.INFO, "");
|
||||
} else {
|
||||
System.out.println(event.getPacketType() + ":");
|
||||
System.out.println(hexDump);
|
||||
System.out.println();
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to log packet " + event.getPacketType() + ":", ex);
|
||||
plugin.getLogger().log(Level.WARNING, "Clearing packet logger...");
|
||||
|
||||
sendingTypes.clear();
|
||||
receivingTypes.clear();
|
||||
startLogging();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getSendingWhitelist() {
|
||||
return sendingWhitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getReceivingWhitelist() {
|
||||
return receivingWhitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
private enum LogLocation {
|
||||
CONSOLE, FILE
|
||||
}
|
||||
|
||||
private static class LogFormatter extends Formatter {
|
||||
private static final SimpleDateFormat DATE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
|
||||
private static final String FORMAT = "[{0}] {1}";
|
||||
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
String string = formatMessage(record);
|
||||
if (string.isEmpty()) {
|
||||
return LINE_SEPARATOR;
|
||||
}
|
||||
|
||||
StringBuilder message = new StringBuilder();
|
||||
message.append(MessageFormat.format(FORMAT, DATE.format(record.getMillis()), string));
|
||||
message.append(LINE_SEPARATOR);
|
||||
return message.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,98 +17,87 @@
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.injector.netty.WirePacket;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents a object capable of sending or receiving packets.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface PacketStream {
|
||||
/**
|
||||
* Send a packet to the given player.
|
||||
* @param receiver - the reciever.
|
||||
* @param packet - packet to send.
|
||||
* @throws InvocationTargetException - if an error occured when sending the packet.
|
||||
*/
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet)
|
||||
throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Send a packet to the given player.
|
||||
*
|
||||
* @param receiver - the reciever.
|
||||
* @param packet - packet to send.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
* @throws InvocationTargetException - if an error occured when sending the packet.
|
||||
* @param packet - packet to send.
|
||||
*/
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters)
|
||||
throws InvocationTargetException;
|
||||
|
||||
void sendServerPacket(Player receiver, PacketContainer packet);
|
||||
|
||||
/**
|
||||
* Send a packet to the given player.
|
||||
* @param receiver - the receiver.
|
||||
* @param packet - packet to send.
|
||||
* @param marker - the network marker to use.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
* @throws InvocationTargetException - if an error occured when sending the packet.
|
||||
*
|
||||
* @param receiver - the reciever.
|
||||
* @param packet - packet to send.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
*/
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters)
|
||||
throws InvocationTargetException;
|
||||
void sendServerPacket(Player receiver, PacketContainer packet, boolean filters);
|
||||
|
||||
/**
|
||||
* Send a packet to the given player.
|
||||
*
|
||||
* @param receiver - the receiver.
|
||||
* @param packet - packet to send.
|
||||
* @param marker - the network marker to use.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
*/
|
||||
void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters);
|
||||
|
||||
/**
|
||||
* Send a wire packet to the given player.
|
||||
*
|
||||
* @param receiver - the receiver.
|
||||
* @param id - packet id.
|
||||
* @param bytes - packet bytes.
|
||||
* @throws InvocationTargetException if an error occured when sending the packet.
|
||||
* @param id - packet id.
|
||||
* @param bytes - packet bytes.
|
||||
*/
|
||||
public void sendWirePacket(Player receiver, int id, byte[] bytes) throws InvocationTargetException;
|
||||
void sendWirePacket(Player receiver, int id, byte[] bytes);
|
||||
|
||||
/**
|
||||
* Send a wire packet to the given player.
|
||||
*
|
||||
* @param receiver - the receiver.
|
||||
* @param packet - packet to send.
|
||||
* @throws InvocationTargetException if an error occured when sending the packet.
|
||||
* @param packet - packet to send.
|
||||
*/
|
||||
public void sendWirePacket(Player receiver, WirePacket packet) throws InvocationTargetException;
|
||||
void sendWirePacket(Player receiver, WirePacket packet);
|
||||
|
||||
/**
|
||||
* Simulate recieving a certain packet from a given player.
|
||||
*
|
||||
* @param sender - the sender.
|
||||
* @param packet - the packet that was sent.
|
||||
* @throws InvocationTargetException If the reflection machinery failed.
|
||||
* @throws IllegalAccessException If the underlying method caused an error.
|
||||
*/
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet)
|
||||
throws IllegalAccessException, InvocationTargetException;
|
||||
void receiveClientPacket(Player sender, PacketContainer packet);
|
||||
|
||||
/**
|
||||
* Simulate recieving a certain packet from a given player.
|
||||
* @param sender - the sender.
|
||||
* @param packet - the packet that was sent.
|
||||
*
|
||||
* @param sender - the sender.
|
||||
* @param packet - the packet that was sent.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
* @throws InvocationTargetException If the reflection machinery failed.
|
||||
* @throws IllegalAccessException If the underlying method caused an error.
|
||||
*/
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
|
||||
throws IllegalAccessException, InvocationTargetException;
|
||||
|
||||
void receiveClientPacket(Player sender, PacketContainer packet, boolean filters);
|
||||
|
||||
/**
|
||||
* Simulate recieving a certain packet from a given player.
|
||||
* @param sender - the sender.
|
||||
* @param packet - the packet that was sent.
|
||||
* @param marker - the network marker to use.
|
||||
*
|
||||
* @param sender - the sender.
|
||||
* @param packet - the packet that was sent.
|
||||
* @param marker - the network marker to use.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
* @throws InvocationTargetException If the reflection machinery failed.
|
||||
* @throws IllegalAccessException If the underlying method caused an error.
|
||||
*/
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet, NetworkMarker marker, boolean filters)
|
||||
throws IllegalAccessException, InvocationTargetException;
|
||||
void receiveClientPacket(Player sender, PacketContainer packet, NetworkMarker marker, boolean filters);
|
||||
}
|
||||
@@ -11,13 +11,11 @@ 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;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.apache.commons.lang.WordUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -52,6 +50,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
public static class Client extends PacketTypeEnum {
|
||||
private final static Sender SENDER = Sender.CLIENT;
|
||||
|
||||
@ForceAsync
|
||||
public static final PacketType SET_PROTOCOL = new PacketType(PROTOCOL, SENDER, 0x00, "SetProtocol", "C00Handshake");
|
||||
|
||||
private final static Client INSTANCE = new Client();
|
||||
@@ -105,59 +104,59 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
|
||||
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x00, "SpawnEntity", "SPacketSpawnObject");
|
||||
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x01, "SpawnEntityExperienceOrb", "SPacketSpawnExperienceOrb");
|
||||
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 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");
|
||||
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 NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x02, "NamedEntitySpawn", "SPacketSpawnPlayer");
|
||||
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x03, "Animation", "SPacketAnimation");
|
||||
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x04, "Statistic", "SPacketStatistics");
|
||||
public static final PacketType BLOCK_CHANGED_ACK = new PacketType(PROTOCOL, SENDER, 0x05, "BlockChangedAck");
|
||||
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, "BlockBreakAnimation", "SPacketBlockBreakAnim");
|
||||
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x07, "TileEntityData", "SPacketUpdateTileEntity");
|
||||
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x08, "BlockAction", "SPacketBlockAction");
|
||||
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x09, "BlockChange", "SPacketBlockChange");
|
||||
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0A, "Boss", "SPacketUpdateBossInfo");
|
||||
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0B, "ServerDifficulty", "SPacketServerDifficulty");
|
||||
public static final PacketType CLEAR_TITLES = new PacketType(PROTOCOL, SENDER, 0x0C, "ClearTitles");
|
||||
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0D, "TabComplete", "SPacketTabComplete");
|
||||
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x0E, "Commands");
|
||||
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0F, "CloseWindow", "SPacketCloseWindow");
|
||||
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x10, "WindowItems", "SPacketWindowItems");
|
||||
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x11, "WindowData", "SPacketWindowProperty");
|
||||
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x12, "SetSlot", "SPacketSetSlot");
|
||||
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x13, "SetCooldown", "SPacketCooldown");
|
||||
public static final PacketType CUSTOM_CHAT_COMPLETIONS = new PacketType(PROTOCOL, SENDER, 0x14, "CustomChatCompletions");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x15, "CustomPayload", "SPacketCustomPayload");
|
||||
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x16, "DeleteChat");
|
||||
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x17, "KickDisconnect", "SPacketDisconnect");
|
||||
public static final PacketType DISGUISED_CHAT = new PacketType(PROTOCOL, SENDER, 0x18, "DisguisedChat");
|
||||
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x19, "EntityStatus", "SPacketEntityStatus");
|
||||
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1A, "Explosion", "SPacketExplosion");
|
||||
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1B, "UnloadChunk", "SPacketUnloadChunk");
|
||||
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1C, "GameStateChange", "SPacketChangeGameState");
|
||||
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x1D, "OpenWindowHorse");
|
||||
public static final PacketType INITIALIZE_BORDER = new PacketType(PROTOCOL, SENDER, 0x1E, "InitializeBorder");
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x1F, "KeepAlive", "SPacketKeepAlive");
|
||||
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x20, "LevelChunkWithLight", "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", "Entity$PacketPlayOutRelEntityMove");
|
||||
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x28, "Entity$RelEntityMoveLook", "Entity$PacketPlayOutRelEntityMoveLook");
|
||||
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x29, "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
|
||||
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2A, "VehicleMove", "SPacketMoveVehicle");
|
||||
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x2B, "OpenBook");
|
||||
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x2C, "OpenWindow", "SPacketOpenWindow");
|
||||
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2D, "OpenSignEditor", "SPacketSignEditorOpen");
|
||||
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x2E, "Ping");
|
||||
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 CHAT = new PacketType(PROTOCOL, SENDER, 0x31, "PlayerChat", "Chat", "SPacketChat");
|
||||
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x32, "PlayerCombatEnd");
|
||||
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x33, "PlayerCombatEnter");
|
||||
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x34, "PlayerCombatKill");
|
||||
public static final PacketType PLAYER_INFO_REMOVE = new PacketType(PROTOCOL, SENDER, 0x35, "PlayerInfoRemove");
|
||||
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x36, "PlayerInfoUpdate", "PlayerInfo");
|
||||
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");
|
||||
@@ -168,44 +167,48 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
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 SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x57, "SetSubtitleText");
|
||||
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x58, "UpdateTime", "SPacketTimeUpdate");
|
||||
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x59, "SetTitleText");
|
||||
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x5A, "SetTitlesAnimation");
|
||||
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x5B, "EntitySound", "SPacketSoundEffect");
|
||||
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x5C, "NamedSoundEffect");
|
||||
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x5D, "StopSound");
|
||||
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x5E, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
|
||||
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x5F, "NBTQuery");
|
||||
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x60, "Collect", "SPacketCollectItem");
|
||||
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x61, "EntityTeleport", "SPacketEntityTeleport");
|
||||
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x62, "Advancements", "SPacketAdvancementInfo");
|
||||
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x63, "UpdateAttributes", "SPacketEntityProperties");
|
||||
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x64, "EntityEffect", "SPacketEntityEffect");
|
||||
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x65, "RecipeUpdate");
|
||||
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x66, "Tags");
|
||||
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x41, "ServerData");
|
||||
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x42, "SetActionBarText");
|
||||
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x43, "SetBorderCenter");
|
||||
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x44, "SetBorderLerpSize");
|
||||
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x45, "SetBorderSize");
|
||||
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x46, "SetBorderWarningDelay");
|
||||
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x47, "SetBorderWarningDistance");
|
||||
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x48, "Camera", "SPacketCamera");
|
||||
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x49, "HeldItemSlot", "SPacketHeldItemChange");
|
||||
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x4A, "ViewCentre");
|
||||
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x4B, "ViewDistance");
|
||||
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x4C, "SpawnPosition", "SPacketSpawnPosition");
|
||||
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x4D, "ScoreboardDisplayObjective", "SPacketDisplayObjective");
|
||||
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x4E, "EntityMetadata", "SPacketEntityMetadata");
|
||||
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x4F, "AttachEntity", "SPacketEntityAttach");
|
||||
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x50, "EntityVelocity", "SPacketEntityVelocity");
|
||||
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x51, "EntityEquipment", "SPacketEntityEquipment");
|
||||
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x52, "Experience", "SPacketSetExperience");
|
||||
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x53, "UpdateHealth", "SPacketUpdateHealth");
|
||||
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x54, "ScoreboardObjective", "SPacketScoreboardObjective");
|
||||
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x55, "Mount", "SPacketSetPassengers");
|
||||
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x56, "ScoreboardTeam", "SPacketTeams");
|
||||
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x57, "ScoreboardScore", "SPacketUpdateScore");
|
||||
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x58, "SetSimulationDistance");
|
||||
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x59, "SetSubtitleText");
|
||||
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x5A, "UpdateTime", "SPacketTimeUpdate");
|
||||
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5B, "SetTitleText");
|
||||
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x5C, "SetTitlesAnimation");
|
||||
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x5D, "EntitySound", "SPacketSoundEffect");
|
||||
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x5E, "NamedSoundEffect");
|
||||
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x5F, "StopSound");
|
||||
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x60, "SystemChat");
|
||||
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x61, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
|
||||
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x62, "NBTQuery");
|
||||
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x63, "Collect", "SPacketCollectItem");
|
||||
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x64, "EntityTeleport", "SPacketEntityTeleport");
|
||||
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x65, "Advancements", "SPacketAdvancementInfo");
|
||||
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x66, "UpdateAttributes", "SPacketEntityProperties");
|
||||
public static final PacketType UPDATE_ENABLED_FEATURES = new PacketType(PROTOCOL, SENDER, 0x67, "UpdateEnabledFeatures");
|
||||
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x68, "EntityEffect", "SPacketEntityEffect");
|
||||
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x69, "RecipeUpdate");
|
||||
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x6A, "Tags");
|
||||
|
||||
// ---- Removed in 1.9
|
||||
|
||||
@@ -312,6 +315,54 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
@Deprecated
|
||||
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 245, "Entity", "SPacketEntity");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType SPAWN_ENTITY_LIVING = new PacketType(PROTOCOL, SENDER, 244, "SpawnEntityLiving", "SPacketSpawnMob");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType SPAWN_ENTITY_PAINTING = new PacketType(PROTOCOL, SENDER, 243, "SpawnEntityPainting", "SPacketSpawnPainting");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType ADD_VIBRATION_SIGNAL = new PacketType(PROTOCOL, SENDER, 242, "AddVibrationSignal");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType BLOCK_BREAK = new PacketType(PROTOCOL, SENDER, 241, "BlockBreak");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19.3
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x0C, "ChatPreview");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19.3
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType PLAYER_CHAT_HEADER = new PacketType(PROTOCOL, SENDER, 0x32, "PlayerChatHeader");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19.3
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType SET_DISPLAY_CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x4E, "SetDisplayChatPreview");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19.3
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x16, "CustomSoundEffect", "SPacketCustomSound");
|
||||
|
||||
private final static Server INSTANCE = new Server();
|
||||
|
||||
// Prevent accidental construction
|
||||
@@ -335,51 +386,54 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, "TeleportAccept", "CPacketConfirmTeleport");
|
||||
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x01, "TileNBTQuery");
|
||||
public static final PacketType DIFFICULTY_CHANGE = new PacketType(PROTOCOL, SENDER, 0x02, "DifficultyChange");
|
||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x03, "Chat", "CPacketChatMessage");
|
||||
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 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");
|
||||
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x21, "ResourcePackStatus", "CPacketResourcePackStatus");
|
||||
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x22, "Advancements", "CPacketSeenAdvancements");
|
||||
public static final PacketType TR_SEL = new PacketType(PROTOCOL, SENDER, 0x23, "TrSel");
|
||||
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x24, "Beacon");
|
||||
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x25, "HeldItemSlot", "CPacketHeldItemChange");
|
||||
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x26, "SetCommandBlock");
|
||||
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x27, "SetCommandMinecart");
|
||||
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x28, "SetCreativeSlot", "CPacketCreativeInventoryAction");
|
||||
public static final PacketType SET_JIGSAW = new PacketType(PROTOCOL, SENDER, 0x29, "SetJigsaw");
|
||||
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x2A, "Struct");
|
||||
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x2B, "UpdateSign", "CPacketUpdateSign");
|
||||
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x2C, "ArmAnimation", "CPacketAnimation");
|
||||
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x2D, "Spectate", "CPacketSpectate");
|
||||
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");
|
||||
public static final PacketType CHAT_ACK = new PacketType(PROTOCOL, SENDER, 0x03, "ChatAck");
|
||||
public static final PacketType CHAT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x04, "ChatCommand");
|
||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x05, "Chat", "CPacketChatMessage");
|
||||
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x06, "ClientCommand", "CPacketClientStatus");
|
||||
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x07, "Settings", "CPacketClientSettings");
|
||||
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x08, "TabComplete", "CPacketTabComplete");
|
||||
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x09, "EnchantItem", "CPacketEnchantItem");
|
||||
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x0A, "WindowClick", "CPacketClickWindow");
|
||||
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0B, "CloseWindow", "CPacketCloseWindow");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0C, "CustomPayload", "CPacketCustomPayload");
|
||||
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x0D, "BEdit");
|
||||
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0E, "EntityNBTQuery");
|
||||
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0F, "UseEntity", "CPacketUseEntity");
|
||||
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x10, "JigsawGenerate");
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x11, "KeepAlive", "CPacketKeepAlive");
|
||||
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x12, "DifficultyLock");
|
||||
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x13, "Flying$PacketPlayInPosition", "Flying$Position", "CPacketPlayer$Position");
|
||||
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x14, "Flying$PacketPlayInPositionLook", "Flying$PositionLook", "CPacketPlayer$PositionRotation");
|
||||
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x15, "Flying$PacketPlayInLook", "Flying$Look", "CPacketPlayer$Rotation");
|
||||
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x16, "Flying$d");
|
||||
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x17, "VehicleMove", "CPacketVehicleMove");
|
||||
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x18, "BoatMove", "CPacketSteerBoat");
|
||||
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x19, "PickItem");
|
||||
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x1A, "AutoRecipe", "CPacketPlaceRecipe");
|
||||
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x1B, "Abilities", "CPacketPlayerAbilities");
|
||||
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x1C, "BlockDig", "CPacketPlayerDigging");
|
||||
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x1D, "EntityAction", "CPacketEntityAction");
|
||||
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1E, "SteerVehicle", "CPacketInput");
|
||||
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x1F, "Pong");
|
||||
public static final PacketType CHAT_SESSION_UPDATE = new PacketType(PROTOCOL, SENDER, 0x20, "ChatSessionUpdate");
|
||||
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x21, "RecipeSettings");
|
||||
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x22, "RecipeDisplayed", "CPacketRecipeInfo");
|
||||
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x23, "ItemName");
|
||||
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x24, "ResourcePackStatus", "CPacketResourcePackStatus");
|
||||
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x25, "Advancements", "CPacketSeenAdvancements");
|
||||
public static final PacketType TR_SEL = new PacketType(PROTOCOL, SENDER, 0x26, "TrSel");
|
||||
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x27, "Beacon");
|
||||
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x28, "HeldItemSlot", "CPacketHeldItemChange");
|
||||
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x29, "SetCommandBlock");
|
||||
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x2A, "SetCommandMinecart");
|
||||
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x2B, "SetCreativeSlot", "CPacketCreativeInventoryAction");
|
||||
public static final PacketType SET_JIGSAW = new PacketType(PROTOCOL, SENDER, 0x2C, "SetJigsaw");
|
||||
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x2D, "Struct");
|
||||
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x2E, "UpdateSign", "CPacketUpdateSign");
|
||||
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x2F, "ArmAnimation", "CPacketAnimation");
|
||||
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x30, "Spectate", "CPacketSpectate");
|
||||
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x31, "UseItem", "CPacketPlayerTryUseItemOnBlock");
|
||||
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x32, "BlockPlace", "CPacketPlayerTryUseItem");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.17
|
||||
@@ -393,6 +447,12 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
@Deprecated
|
||||
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 254, "Flying", "CPacketPlayer");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19.3
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x06, "ChatPreview");
|
||||
|
||||
private final static Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
@@ -427,6 +487,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
|
||||
@ForceAsync
|
||||
public static final PacketType SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, "ServerInfo", "SPacketServerInfo");
|
||||
@ForceAsync
|
||||
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x01, "Pong", "SPacketPong");
|
||||
|
||||
/**
|
||||
@@ -457,6 +518,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
private final static Sender SENDER = Sender.CLIENT;
|
||||
|
||||
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, "Start", "CPacketServerQuery");
|
||||
@ForceAsync
|
||||
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x01, "Ping", "CPacketPing");
|
||||
|
||||
private final static Client INSTANCE = new Client();
|
||||
@@ -654,7 +716,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
/**
|
||||
* Protocol version of all the current IDs.
|
||||
*/
|
||||
private static final MinecraftVersion PROTOCOL_VERSION = Constants.CURRENT_VERSION;
|
||||
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.LATEST;
|
||||
|
||||
private final Protocol protocol;
|
||||
private final Sender sender;
|
||||
@@ -694,7 +756,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* @return Every packet type.
|
||||
*/
|
||||
public static Iterable<PacketType> values() {
|
||||
List<Iterable<? extends PacketType>> sources = Lists.newArrayList();
|
||||
final List<Iterable<? extends PacketType>> sources = new ArrayList<>();
|
||||
|
||||
sources.add(Handshake.Client.getInstance());
|
||||
sources.add(Handshake.Server.getInstance());
|
||||
sources.add(Play.Client.getInstance());
|
||||
|
||||
@@ -23,11 +23,6 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Represents a more modern object-based enum.
|
||||
* <p>
|
||||
@@ -37,7 +32,7 @@ import com.google.common.collect.Sets;
|
||||
*/
|
||||
public class PacketTypeEnum implements Iterable<PacketType> {
|
||||
// Used to convert between IDs and names
|
||||
protected Set<PacketType> members = Sets.newHashSet();
|
||||
protected Set<PacketType> members = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Registers every declared PacketType field.
|
||||
|
||||
@@ -19,14 +19,14 @@ import com.google.common.collect.Multimap;
|
||||
class PacketTypeLookup {
|
||||
public static class ProtocolSenderLookup {
|
||||
// Unroll lookup for performance reasons
|
||||
public final IntegerMap<PacketType> HANDSHAKE_CLIENT = IntegerMap.newMap();
|
||||
public final IntegerMap<PacketType> HANDSHAKE_SERVER = IntegerMap.newMap();
|
||||
public final IntegerMap<PacketType> GAME_CLIENT = IntegerMap.newMap();
|
||||
public final IntegerMap<PacketType> GAME_SERVER = IntegerMap.newMap();
|
||||
public final IntegerMap<PacketType> STATUS_CLIENT = IntegerMap.newMap();
|
||||
public final IntegerMap<PacketType> STATUS_SERVER = IntegerMap.newMap();
|
||||
public final IntegerMap<PacketType> LOGIN_CLIENT = IntegerMap.newMap();
|
||||
public final IntegerMap<PacketType> LOGIN_SERVER = IntegerMap.newMap();
|
||||
public final IntegerMap<PacketType> HANDSHAKE_CLIENT = new IntegerMap<>();
|
||||
public final IntegerMap<PacketType> HANDSHAKE_SERVER = new IntegerMap<>();
|
||||
public final IntegerMap<PacketType> GAME_CLIENT = new IntegerMap<>();
|
||||
public final IntegerMap<PacketType> GAME_SERVER = new IntegerMap<>();
|
||||
public final IntegerMap<PacketType> STATUS_CLIENT = new IntegerMap<>();
|
||||
public final IntegerMap<PacketType> STATUS_SERVER = new IntegerMap<>();
|
||||
public final IntegerMap<PacketType> LOGIN_CLIENT = new IntegerMap<>();
|
||||
public final IntegerMap<PacketType> LOGIN_SERVER = new IntegerMap<>();
|
||||
|
||||
/**
|
||||
* Retrieve the correct integer map for a specific protocol and sender.
|
||||
@@ -52,14 +52,14 @@ class PacketTypeLookup {
|
||||
|
||||
public static class ClassLookup {
|
||||
// Unroll lookup for performance reasons
|
||||
public final Map<String, PacketType> HANDSHAKE_CLIENT = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> HANDSHAKE_SERVER = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> GAME_CLIENT = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> GAME_SERVER = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> STATUS_CLIENT = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> STATUS_SERVER = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> LOGIN_CLIENT = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> LOGIN_SERVER = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> HANDSHAKE_CLIENT = new ConcurrentHashMap<>();
|
||||
public final Map<String, PacketType> HANDSHAKE_SERVER = new ConcurrentHashMap<>();
|
||||
public final Map<String, PacketType> GAME_CLIENT = new ConcurrentHashMap<>();
|
||||
public final Map<String, PacketType> GAME_SERVER = new ConcurrentHashMap<>();
|
||||
public final Map<String, PacketType> STATUS_CLIENT = new ConcurrentHashMap<>();
|
||||
public final Map<String, PacketType> STATUS_SERVER = new ConcurrentHashMap<>();
|
||||
public final Map<String, PacketType> LOGIN_CLIENT = new ConcurrentHashMap<>();
|
||||
public final Map<String, PacketType> LOGIN_SERVER = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Retrieve the correct integer map for a specific protocol and sender.
|
||||
@@ -84,9 +84,9 @@ class PacketTypeLookup {
|
||||
}
|
||||
|
||||
// Packet IDs from 1.6.4 and below
|
||||
private final IntegerMap<PacketType> legacyLookup = new IntegerMap<PacketType>();
|
||||
private final IntegerMap<PacketType> serverLookup = new IntegerMap<PacketType>();
|
||||
private final IntegerMap<PacketType> clientLookup = new IntegerMap<PacketType>();
|
||||
private final IntegerMap<PacketType> legacyLookup = new IntegerMap<>();
|
||||
private final IntegerMap<PacketType> serverLookup = new IntegerMap<>();
|
||||
private final IntegerMap<PacketType> clientLookup = new IntegerMap<>();
|
||||
|
||||
// Packets for 1.7.2
|
||||
private final ProtocolSenderLookup idLookup = new ProtocolSenderLookup();
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.google.common.collect.ContiguousSet;
|
||||
import com.google.common.collect.DiscreteDomain;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
class PacketTypeParser {
|
||||
public final static Range<Integer> DEFAULT_MAX_RANGE = Range.closed(0, 255);
|
||||
public static final Range<Integer> DEFAULT_MAX_RANGE = Range.closed(0, 255);
|
||||
|
||||
private Sender side = null;
|
||||
private Protocol protocol = null;
|
||||
|
||||
public Set<PacketType> parseTypes(Deque<String> arguments, Range<Integer> defaultRange) {
|
||||
Set<PacketType> result = Sets.newHashSet();
|
||||
final Set<PacketType> result = new HashSet<>();
|
||||
side = null;
|
||||
protocol = null;
|
||||
|
||||
@@ -68,8 +68,7 @@ class PacketTypeParser {
|
||||
|
||||
// Supply a default integer range
|
||||
if (ranges.isEmpty() && result.isEmpty()) {
|
||||
ranges = Lists.newArrayList();
|
||||
ranges.add(defaultRange);
|
||||
ranges = Collections.singletonList(defaultRange);
|
||||
}
|
||||
|
||||
for (Range<Integer> range : ranges) {
|
||||
@@ -130,19 +129,20 @@ class PacketTypeParser {
|
||||
* @return The protocol, or NULL if not found.
|
||||
*/
|
||||
public Protocol parseProtocol(String text) {
|
||||
if (text == null)
|
||||
return null;
|
||||
String candidate = text.toLowerCase();
|
||||
|
||||
if ("handshake".equals(candidate) || "handshaking".equals(candidate))
|
||||
return Protocol.HANDSHAKING;
|
||||
else if ("login".equals(candidate))
|
||||
return Protocol.LOGIN;
|
||||
else if ("play".equals(candidate) || "game".equals(candidate))
|
||||
return Protocol.PLAY;
|
||||
else if ("status".equals(candidate))
|
||||
return Protocol.STATUS;
|
||||
else
|
||||
return null;
|
||||
if (text == null) return null;
|
||||
|
||||
switch (text.toLowerCase()) {
|
||||
case "handshake":
|
||||
case "handshaking":
|
||||
return Protocol.HANDSHAKING;
|
||||
case "login":
|
||||
return Protocol.LOGIN;
|
||||
case "play":
|
||||
case "game":
|
||||
return Protocol.PLAY;
|
||||
case "status":
|
||||
return Protocol.STATUS;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,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
|
||||
* 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;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.io.Files;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.injector.PlayerInjectHooks;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.Files;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Represents the configuration of ProtocolLib.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ProtocolConfig {
|
||||
|
||||
private static final String LAST_UPDATE_FILE = "lastupdate";
|
||||
|
||||
private static final String SECTION_GLOBAL = "global";
|
||||
@@ -46,11 +41,10 @@ public class ProtocolConfig {
|
||||
private static final String METRICS_ENABLED = "metrics";
|
||||
|
||||
private static final String IGNORE_VERSION_CHECK = "ignore version check";
|
||||
private static final String BACKGROUND_COMPILER_ENABLED = "background compiler";
|
||||
|
||||
private static final String DEBUG_MODE_ENABLED = "debug";
|
||||
private static final String DETAILED_ERROR = "detailed error";
|
||||
private static final String INJECTION_METHOD = "injection method";
|
||||
private static final String CHAT_WARNINGS = "chat warnings";
|
||||
|
||||
private static final String SCRIPT_ENGINE_NAME = "script engine";
|
||||
private static final String SUPPRESSED_REPORTS = "suppressed reports";
|
||||
@@ -98,7 +92,7 @@ public class ProtocolConfig {
|
||||
|
||||
/**
|
||||
* Load the last update time stamp from the file system.
|
||||
*
|
||||
*
|
||||
* @return Last update time stamp.
|
||||
*/
|
||||
private long loadLastUpdate() {
|
||||
@@ -106,7 +100,7 @@ public class ProtocolConfig {
|
||||
|
||||
if (dataFile.exists()) {
|
||||
try {
|
||||
return Long.parseLong(Files.toString(dataFile, Charsets.UTF_8));
|
||||
return Long.parseLong(Files.toString(dataFile, StandardCharsets.UTF_8));
|
||||
} catch (NumberFormatException e) {
|
||||
plugin.getLogger().warning("Cannot parse " + dataFile + " as a number.");
|
||||
} catch (IOException e) {
|
||||
@@ -119,7 +113,7 @@ public class ProtocolConfig {
|
||||
|
||||
/**
|
||||
* Store the given time stamp.
|
||||
*
|
||||
*
|
||||
* @param value - time stamp to store.
|
||||
*/
|
||||
private void saveLastUpdate(long value) {
|
||||
@@ -128,11 +122,12 @@ public class ProtocolConfig {
|
||||
// The data folder must exist
|
||||
dataFile.getParentFile().mkdirs();
|
||||
|
||||
if (dataFile.exists())
|
||||
if (dataFile.exists()) {
|
||||
dataFile.delete();
|
||||
}
|
||||
|
||||
try {
|
||||
Files.write(Long.toString(value), dataFile, Charsets.UTF_8);
|
||||
Files.write(Long.toString(value), dataFile, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot write " + dataFile, e);
|
||||
}
|
||||
@@ -140,7 +135,7 @@ public class ProtocolConfig {
|
||||
|
||||
/**
|
||||
* Retrieve the file that is used to store the update time stamp.
|
||||
*
|
||||
*
|
||||
* @return File storing the update time stamp.
|
||||
*/
|
||||
private File getLastUpdateFile() {
|
||||
@@ -149,7 +144,7 @@ public class ProtocolConfig {
|
||||
|
||||
/**
|
||||
* Load data sections.
|
||||
*
|
||||
*
|
||||
* @param copyDefaults - whether or not to copy configuration defaults.
|
||||
*/
|
||||
private void loadSections(boolean copyDefaults) {
|
||||
@@ -167,8 +162,9 @@ public class ProtocolConfig {
|
||||
if (copyDefaults && (!getFile().exists() || global == null || updater == null)) {
|
||||
loadingSections = true;
|
||||
|
||||
if (config != null)
|
||||
if (config != null) {
|
||||
config.options().copyDefaults(true);
|
||||
}
|
||||
plugin.saveDefaultConfig();
|
||||
plugin.reloadConfig();
|
||||
loadingSections = false;
|
||||
@@ -180,7 +176,7 @@ public class ProtocolConfig {
|
||||
|
||||
/**
|
||||
* Set a particular configuration key value pair.
|
||||
*
|
||||
*
|
||||
* @param section - the configuration root.
|
||||
* @param path - the path to the key.
|
||||
* @param value - the value to set.
|
||||
@@ -210,7 +206,7 @@ public class ProtocolConfig {
|
||||
|
||||
/**
|
||||
* Retrieve a reference to the configuration file.
|
||||
*
|
||||
*
|
||||
* @return Configuration file on disk.
|
||||
*/
|
||||
public File getFile() {
|
||||
@@ -219,7 +215,7 @@ public class ProtocolConfig {
|
||||
|
||||
/**
|
||||
* Determine if detailed error reporting is enabled. Default FALSE.
|
||||
*
|
||||
*
|
||||
* @return TRUE if it is enabled, FALSE otherwise.
|
||||
*/
|
||||
public boolean isDetailedErrorReporting() {
|
||||
@@ -227,57 +223,36 @@ public class ProtocolConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not detailed error reporting is enabled.
|
||||
*
|
||||
* @param value - TRUE if it is enabled, FALSE otherwise.
|
||||
* Print warnings to players with protocol.info
|
||||
* @return true if enabled, false if not
|
||||
*/
|
||||
public void setDetailedErrorReporting(boolean value) {
|
||||
global.set(DETAILED_ERROR, value);
|
||||
public boolean isChatWarnings() {
|
||||
return getGlobalValue(CHAT_WARNINGS, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether or not ProtocolLib should determine if a new version has been released.
|
||||
*
|
||||
*
|
||||
* @return TRUE if it should do this automatically, FALSE otherwise.
|
||||
*/
|
||||
public boolean isAutoNotify() {
|
||||
return getUpdaterValue(UPDATER_NOTIFY, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not ProtocolLib should determine if a new version has been released.
|
||||
*
|
||||
* @param value - TRUE to do this automatically, FALSE otherwise.
|
||||
*/
|
||||
public void setAutoNotify(boolean value) {
|
||||
setConfig(updater, UPDATER_NOTIFY, value);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether or not ProtocolLib should automatically download the new version.
|
||||
*
|
||||
*
|
||||
* @return TRUE if it should, FALSE otherwise.
|
||||
*/
|
||||
public boolean isAutoDownload() {
|
||||
return updater != null && getUpdaterValue(UPDATER_DOWNLAD, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not ProtocolLib should automatically download the new version.
|
||||
*
|
||||
* @param value - TRUE if it should. FALSE otherwise.
|
||||
*/
|
||||
public void setAutoDownload(boolean value) {
|
||||
setConfig(updater, UPDATER_DOWNLAD, value);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not debug mode is enabled.
|
||||
* <p>
|
||||
* This grants access to the filter command.
|
||||
*
|
||||
*
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean isDebug() {
|
||||
@@ -286,7 +261,7 @@ public class ProtocolConfig {
|
||||
|
||||
/**
|
||||
* Set whether or not debug mode is enabled.
|
||||
*
|
||||
*
|
||||
* @param value - TRUE if it is enabled, FALSE otherwise.
|
||||
*/
|
||||
public void setDebug(boolean value) {
|
||||
@@ -296,26 +271,16 @@ public class ProtocolConfig {
|
||||
|
||||
/**
|
||||
* Retrieve an immutable list of every suppressed report type.
|
||||
*
|
||||
*
|
||||
* @return Every suppressed report type.
|
||||
*/
|
||||
public ImmutableList<String> getSuppressedReports() {
|
||||
return ImmutableList.copyOf(getGlobalValue(SUPPRESSED_REPORTS, new ArrayList<String>()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of suppressed report types,
|
||||
*
|
||||
* @param reports - suppressed report types.
|
||||
*/
|
||||
public void setSuppressedReports(List<String> reports) {
|
||||
global.set(SUPPRESSED_REPORTS, Lists.newArrayList(reports));
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the amount of time to wait until checking for a new update.
|
||||
*
|
||||
*
|
||||
* @return The amount of time to wait.
|
||||
*/
|
||||
public long getAutoDelay() {
|
||||
@@ -323,87 +288,27 @@ public class ProtocolConfig {
|
||||
return Math.max(getUpdaterValue(UPDATER_DELAY, 0), DEFAULT_UPDATER_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the amount of time to wait until checking for a new update.
|
||||
* <p>
|
||||
* This time must be greater than 59 seconds.
|
||||
*
|
||||
* @param delaySeconds - the amount of time to wait.
|
||||
*/
|
||||
public void setAutoDelay(long delaySeconds) {
|
||||
// Silently fix the delay
|
||||
if (delaySeconds < DEFAULT_UPDATER_DELAY)
|
||||
delaySeconds = DEFAULT_UPDATER_DELAY;
|
||||
setConfig(updater, UPDATER_DELAY, delaySeconds);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* The version of Minecraft to ignore the built-in safety feature.
|
||||
*
|
||||
*
|
||||
* @return The version to ignore ProtocolLib's satefy.
|
||||
*/
|
||||
public String getIgnoreVersionCheck() {
|
||||
return getGlobalValue(IGNORE_VERSION_CHECK, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets under which version of Minecraft the version safety feature will be ignored.
|
||||
* <p>
|
||||
* This is useful if a server operator has tested and verified that a version of ProtocolLib works, but doesn't want or can't upgrade to a newer version.
|
||||
*
|
||||
* @param ignoreVersion - the version of Minecraft where the satefy will be disabled.
|
||||
*/
|
||||
public void setIgnoreVersionCheck(String ignoreVersion) {
|
||||
setConfig(global, IGNORE_VERSION_CHECK, ignoreVersion);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether or not metrics is enabled.
|
||||
*
|
||||
*
|
||||
* @return TRUE if metrics is enabled, FALSE otherwise.
|
||||
*/
|
||||
public boolean isMetricsEnabled() {
|
||||
return getGlobalValue(METRICS_ENABLED, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not metrics is enabled.
|
||||
* <p>
|
||||
* This setting will take effect next time ProtocolLib is started.
|
||||
*
|
||||
* @param enabled - whether or not metrics is enabled.
|
||||
*/
|
||||
public void setMetricsEnabled(boolean enabled) {
|
||||
setConfig(global, METRICS_ENABLED, enabled);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether or not the background compiler for structure modifiers is enabled or not.
|
||||
*
|
||||
* @return TRUE if it is enabled, FALSE otherwise.
|
||||
*/
|
||||
public boolean isBackgroundCompilerEnabled() {
|
||||
return getGlobalValue(BACKGROUND_COMPILER_ENABLED, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not the background compiler for structure modifiers is enabled or not.
|
||||
* <p>
|
||||
* This setting will take effect next time ProtocolLib is started.
|
||||
*
|
||||
* @param enabled - TRUE if is enabled/running, FALSE otherwise.
|
||||
*/
|
||||
public void setBackgroundCompilerEnabled(boolean enabled) {
|
||||
setConfig(global, BACKGROUND_COMPILER_ENABLED, enabled);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last time we updated, in seconds since 1970.01.01 00:00.
|
||||
*
|
||||
*
|
||||
* @return Last update time.
|
||||
*/
|
||||
public long getAutoLastTime() {
|
||||
@@ -414,7 +319,7 @@ public class ProtocolConfig {
|
||||
* Set the last time we updated, in seconds since 1970.01.01 00:00.
|
||||
* <p>
|
||||
* Note that this is not considered to modify the configuration, so the modification count will not be incremented.
|
||||
*
|
||||
*
|
||||
* @param lastTimeSeconds - new last update time.
|
||||
*/
|
||||
public void setAutoLastTime(long lastTimeSeconds) {
|
||||
@@ -424,7 +329,7 @@ public class ProtocolConfig {
|
||||
|
||||
/**
|
||||
* Retrieve the unique name of the script engine to use for filtering.
|
||||
*
|
||||
*
|
||||
* @return Script engine to use.
|
||||
*/
|
||||
public String getScriptEngineName() {
|
||||
@@ -435,7 +340,7 @@ public class ProtocolConfig {
|
||||
* Set the unique name of the script engine to use for filtering.
|
||||
* <p>
|
||||
* This setting will take effect next time ProtocolLib is started.
|
||||
*
|
||||
*
|
||||
* @param name - name of the script engine to use.
|
||||
*/
|
||||
public void setScriptEngineName(String name) {
|
||||
@@ -443,45 +348,9 @@ public class ProtocolConfig {
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the default injection method.
|
||||
*
|
||||
* @return Default method.
|
||||
*/
|
||||
public PlayerInjectHooks getDefaultMethod() {
|
||||
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the injection method that has been set in the configuration, or use a default value.
|
||||
*
|
||||
* @return Injection method to use.
|
||||
* @throws IllegalArgumentException If the configuration option is malformed.
|
||||
*/
|
||||
public PlayerInjectHooks getInjectionMethod() throws IllegalArgumentException {
|
||||
String text = global.getString(INJECTION_METHOD);
|
||||
|
||||
// Default hook if nothing has been set
|
||||
PlayerInjectHooks hook = getDefaultMethod();
|
||||
|
||||
if (text != null)
|
||||
hook = PlayerInjectHooks.valueOf(text.toUpperCase(Locale.ENGLISH).replace(" ", "_"));
|
||||
return hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the starting injection method to use.
|
||||
*
|
||||
* @return Injection method.
|
||||
*/
|
||||
public void setInjectionMethod(PlayerInjectHooks hook) {
|
||||
setConfig(global, INJECTION_METHOD, hook.name());
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of modifications made to this configuration.
|
||||
*
|
||||
*
|
||||
* @return The number of modifications.
|
||||
*/
|
||||
public int getModificationCount() {
|
||||
@@ -492,10 +361,12 @@ public class ProtocolConfig {
|
||||
* Save the current configuration file.
|
||||
*/
|
||||
public void saveAll() {
|
||||
if (valuesChanged)
|
||||
if (valuesChanged) {
|
||||
saveLastUpdate(lastUpdateTime);
|
||||
if (configChanged)
|
||||
}
|
||||
if (configChanged) {
|
||||
plugin.saveConfig();
|
||||
}
|
||||
|
||||
// And we're done
|
||||
valuesChanged = false;
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||
import com.comphenix.protocol.error.BasicErrorReporter;
|
||||
import com.comphenix.protocol.error.DelegatedErrorReporter;
|
||||
import com.comphenix.protocol.error.DetailedErrorReporter;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.InternalManager;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||
import com.comphenix.protocol.metrics.Statistics;
|
||||
import com.comphenix.protocol.updater.Updater;
|
||||
import com.comphenix.protocol.updater.Updater.UpdateType;
|
||||
import com.comphenix.protocol.utility.ByteBuddyFactory;
|
||||
import com.comphenix.protocol.utility.ChatExtensions;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.Util;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Iterables;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||
import com.comphenix.protocol.error.*;
|
||||
import com.comphenix.protocol.injector.DelayedSingleTask;
|
||||
import com.comphenix.protocol.injector.InternalManager;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||
import com.comphenix.protocol.injector.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.metrics.Statistics;
|
||||
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||
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;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
@@ -57,111 +57,88 @@ import org.bukkit.plugin.java.JavaPlugin;
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ProtocolLib extends JavaPlugin {
|
||||
|
||||
// Every possible error or warning report type
|
||||
public static final ReportType REPORT_CANNOT_LOAD_CONFIG = new ReportType("Cannot load configuration");
|
||||
public static final ReportType REPORT_CANNOT_DELETE_CONFIG = new ReportType("Cannot delete old ProtocolLib configuration.");
|
||||
public static final ReportType REPORT_CANNOT_PARSE_INJECTION_METHOD = new ReportType("Cannot parse injection method. Using default.");
|
||||
public static final ReportType REPORT_CANNOT_DELETE_CONFIG = new ReportType(
|
||||
"Cannot delete old ProtocolLib configuration.");
|
||||
|
||||
public static final ReportType REPORT_PLUGIN_LOAD_ERROR = new ReportType("Cannot load ProtocolLib.");
|
||||
public static final ReportType REPORT_CANNOT_LOAD_CONFIG = new ReportType("Cannot load configuration");
|
||||
public static final ReportType REPORT_PLUGIN_ENABLE_ERROR = new ReportType("Cannot enable ProtocolLib.");
|
||||
|
||||
public static final ReportType REPORT_METRICS_IO_ERROR = new ReportType("Unable to enable metrics due to network problems.");
|
||||
public static final ReportType REPORT_METRICS_GENERIC_ERROR = new ReportType("Unable to enable metrics due to network problems.");
|
||||
public static final ReportType REPORT_METRICS_IO_ERROR = new ReportType(
|
||||
"Unable to enable metrics due to network problems.");
|
||||
public static final ReportType REPORT_METRICS_GENERIC_ERROR = new ReportType(
|
||||
"Unable to enable metrics due to network problems.");
|
||||
|
||||
public static final ReportType REPORT_CANNOT_PARSE_MINECRAFT_VERSION = new ReportType("Unable to retrieve current Minecraft version. Assuming %s");
|
||||
public static final ReportType REPORT_CANNOT_DETECT_CONFLICTING_PLUGINS = new ReportType("Unable to detect conflicting plugin versions.");
|
||||
public static final ReportType REPORT_CANNOT_PARSE_MINECRAFT_VERSION = new ReportType(
|
||||
"Unable to retrieve current Minecraft version. Assuming %s");
|
||||
public static final ReportType REPORT_CANNOT_DETECT_CONFLICTING_PLUGINS = new ReportType(
|
||||
"Unable to detect conflicting plugin versions.");
|
||||
public static final ReportType REPORT_CANNOT_REGISTER_COMMAND = new ReportType("Cannot register command %s: %s");
|
||||
|
||||
public static final ReportType REPORT_CANNOT_CREATE_TIMEOUT_TASK = new ReportType("Unable to create packet timeout task.");
|
||||
public static final ReportType REPORT_CANNOT_CREATE_TIMEOUT_TASK = new ReportType(
|
||||
"Unable to create packet timeout task.");
|
||||
public static final ReportType REPORT_CANNOT_UPDATE_PLUGIN = new ReportType("Cannot perform automatic updates.");
|
||||
|
||||
// Update information
|
||||
static final String BUKKIT_DEV_SLUG = "protocollib";
|
||||
static final int BUKKIT_DEV_ID = 45564;
|
||||
|
||||
// Different commands
|
||||
private enum ProtocolCommand {
|
||||
FILTER,
|
||||
PACKET,
|
||||
PROTOCOL,
|
||||
LOGGING;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of milliseconds per second.
|
||||
*/
|
||||
static final long MILLI_PER_SECOND = 1000;
|
||||
static final long MILLI_PER_SECOND = TimeUnit.SECONDS.toMillis(1);
|
||||
|
||||
private static final int ASYNC_MANAGER_DELAY = 1;
|
||||
private static final String PERMISSION_INFO = "protocol.info";
|
||||
|
||||
// There should only be one protocol manager, so we'll make it static
|
||||
private static InternalManager protocolManager;
|
||||
|
||||
// Error reporter
|
||||
private static ErrorReporter reporter = new BasicErrorReporter();
|
||||
|
||||
// Strongly typed configuration
|
||||
// these fields are only existing once, we can make them static
|
||||
private static Logger logger;
|
||||
private static ProtocolConfig config;
|
||||
|
||||
// Metrics and statistics
|
||||
private static InternalManager protocolManager;
|
||||
private static ErrorReporter reporter = new BasicErrorReporter();
|
||||
|
||||
private Statistics statistics;
|
||||
|
||||
// Structure compiler
|
||||
private BackgroundCompiler backgroundCompiler;
|
||||
|
||||
// Used to clean up server packets that have expired, but mostly required to simulate
|
||||
// recieving client packets.
|
||||
private int packetTask = -1;
|
||||
private int tickCounter = 0;
|
||||
private static final int ASYNC_MANAGER_DELAY = 1;
|
||||
|
||||
// Used to unhook players after a delay
|
||||
private DelayedSingleTask unhookTask;
|
||||
|
||||
// Settings/options
|
||||
private int configExpectedMod = -1;
|
||||
|
||||
// Updater
|
||||
// updater
|
||||
private Updater updater;
|
||||
public static boolean UPDATES_DISABLED;
|
||||
|
||||
// Logger
|
||||
private static Logger logger;
|
||||
private Handler redirectHandler;
|
||||
|
||||
// Commands
|
||||
// commands
|
||||
private CommandProtocol commandProtocol;
|
||||
private CommandPacket commandPacket;
|
||||
private CommandFilter commandFilter;
|
||||
private PacketLogging packetLogging;
|
||||
|
||||
// Whether or not disable is not needed
|
||||
// Whether disabling field resetting is needed
|
||||
private boolean skipDisable;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Logging
|
||||
logger = getLogger();
|
||||
logger = this.getLogger();
|
||||
ProtocolLogger.init(this);
|
||||
|
||||
// Initialize enhancer factory
|
||||
ByteBuddyFactory.getInstance().setClassLoader(getClassLoader());
|
||||
ByteBuddyFactory.getInstance().setClassLoader(this.getClassLoader());
|
||||
|
||||
// Add global parameters
|
||||
DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this);
|
||||
reporter = getFilteredReporter(detailedReporter);
|
||||
reporter = this.getFilteredReporter(detailedReporter);
|
||||
|
||||
// Configuration
|
||||
saveDefaultConfig();
|
||||
reloadConfig();
|
||||
this.saveDefaultConfig();
|
||||
this.reloadConfig();
|
||||
|
||||
try {
|
||||
config = new ProtocolConfig(this);
|
||||
} catch (Exception e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_LOAD_CONFIG).error(e));
|
||||
} catch (Exception exception) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_LOAD_CONFIG).error(exception));
|
||||
|
||||
// Load it again
|
||||
if (deleteConfig()) {
|
||||
if (this.deleteConfig()) {
|
||||
config = new ProtocolConfig(this);
|
||||
} else {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_DELETE_CONFIG));
|
||||
@@ -171,11 +148,8 @@ 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);
|
||||
@@ -184,52 +158,33 @@ public class ProtocolLib extends JavaPlugin {
|
||||
|
||||
try {
|
||||
// Check for other versions
|
||||
checkConflictingVersions();
|
||||
this.checkConflictingVersions();
|
||||
|
||||
// Handle unexpected Minecraft versions
|
||||
MinecraftVersion version = verifyMinecraftVersion();
|
||||
MinecraftVersion version = this.verifyMinecraftVersion();
|
||||
|
||||
// Set updater - this will not perform any update automatically
|
||||
updater = Updater.create(this, BUKKIT_DEV_ID, getFile(), UpdateType.NO_DOWNLOAD, true);
|
||||
this.updater = Updater.create(this, 0, this.getFile(), UpdateType.NO_DOWNLOAD, true);
|
||||
|
||||
unhookTask = new DelayedSingleTask(this);
|
||||
// api init
|
||||
protocolManager = PacketFilterManager.newBuilder()
|
||||
.classLoader(getClassLoader())
|
||||
.server(getServer())
|
||||
.server(this.getServer())
|
||||
.library(this)
|
||||
.minecraftVersion(version)
|
||||
.unhookTask(unhookTask)
|
||||
.reporter(reporter)
|
||||
.build();
|
||||
|
||||
// Initialize the API
|
||||
ProtocolLibrary.init(this, config, protocolManager, reporter);
|
||||
|
||||
// Setup error reporter
|
||||
detailedReporter.addGlobalParameter("manager", protocolManager);
|
||||
|
||||
// Update injection hook
|
||||
try {
|
||||
PlayerInjectHooks hook = config.getInjectionMethod();
|
||||
|
||||
// Only update the hook if it's different
|
||||
if (!protocolManager.getPlayerHook().equals(hook)) {
|
||||
logger.info("Changing player hook from " + protocolManager.getPlayerHook() + " to " + hook);
|
||||
protocolManager.setPlayerHook(hook);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
reporter.reportWarning(config, Report.newBuilder(REPORT_CANNOT_PARSE_INJECTION_METHOD).error(e));
|
||||
}
|
||||
|
||||
// Send logging information to player listeners too
|
||||
initializeCommands();
|
||||
setupBroadcastUsers(PERMISSION_INFO);
|
||||
this.initializeCommands();
|
||||
this.setupBroadcastUsers(PERMISSION_INFO);
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_PLUGIN_LOAD_ERROR).error(e).callerParam(protocolManager));
|
||||
disablePlugin();
|
||||
this.disablePlugin();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,18 +196,18 @@ public class ProtocolLib extends JavaPlugin {
|
||||
for (ProtocolCommand command : ProtocolCommand.values()) {
|
||||
try {
|
||||
switch (command) {
|
||||
case PROTOCOL:
|
||||
commandProtocol = new CommandProtocol(reporter, this, updater, config);
|
||||
break;
|
||||
case FILTER:
|
||||
commandFilter = new CommandFilter(reporter, this, config);
|
||||
break;
|
||||
case PACKET:
|
||||
commandPacket = new CommandPacket(reporter, this, logger, commandFilter, protocolManager);
|
||||
break;
|
||||
case LOGGING:
|
||||
packetLogging = new PacketLogging(this, protocolManager);
|
||||
break;
|
||||
case PROTOCOL:
|
||||
this.commandProtocol = new CommandProtocol(reporter, this, this.updater, config);
|
||||
break;
|
||||
case FILTER:
|
||||
this.commandFilter = new CommandFilter(reporter, this, config);
|
||||
break;
|
||||
case PACKET:
|
||||
this.commandPacket = new CommandPacket(reporter, this, logger, this.commandFilter, protocolManager);
|
||||
break;
|
||||
case LOGGING:
|
||||
this.packetLogging = new PacketLogging(this, protocolManager);
|
||||
break;
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
@@ -267,12 +222,13 @@ public class ProtocolLib extends JavaPlugin {
|
||||
|
||||
/**
|
||||
* Retrieve a error reporter that may be filtered by the configuration.
|
||||
*
|
||||
* @return The new default error reporter.
|
||||
*/
|
||||
private ErrorReporter getFilteredReporter(ErrorReporter reporter) {
|
||||
return new DelegatedErrorReporter(reporter) {
|
||||
private int lastModCount = -1;
|
||||
private Set<String> reports = Sets.newHashSet();
|
||||
private Set<String> reports = new HashSet<>();
|
||||
|
||||
@Override
|
||||
protected Report filterReport(Object sender, Report report, boolean detailed) {
|
||||
@@ -280,19 +236,20 @@ public class ProtocolLib extends JavaPlugin {
|
||||
String canonicalName = ReportType.getReportName(sender, report.getType());
|
||||
String reportName = Iterables.getLast(Splitter.on("#").split(canonicalName)).toUpperCase();
|
||||
|
||||
if (config != null && config.getModificationCount() != lastModCount) {
|
||||
if (config != null && config.getModificationCount() != this.lastModCount) {
|
||||
// Update our cached set again
|
||||
reports = Sets.newHashSet(config.getSuppressedReports());
|
||||
lastModCount = config.getModificationCount();
|
||||
this.reports = new HashSet<>(config.getSuppressedReports());
|
||||
this.lastModCount = config.getModificationCount();
|
||||
}
|
||||
|
||||
// Cancel reports either on the full canonical name, or just the report name
|
||||
if (reports.contains(canonicalName) || reports.contains(reportName))
|
||||
if (this.reports.contains(canonicalName) || this.reports.contains(reportName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// Only report this with a minor message
|
||||
logger.warning("Error filtering reports: " + e.toString());
|
||||
logger.warning("Error filtering reports: " + e);
|
||||
}
|
||||
// Don't filter anything
|
||||
return report;
|
||||
@@ -316,16 +273,17 @@ public class ProtocolLib extends JavaPlugin {
|
||||
|
||||
private void setupBroadcastUsers(final String permission) {
|
||||
// Guard against multiple calls
|
||||
if (redirectHandler != null)
|
||||
if (this.redirectHandler != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Broadcast information to every user too
|
||||
redirectHandler = new Handler() {
|
||||
this.redirectHandler = new Handler() {
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
// Only display warnings and above
|
||||
if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
|
||||
commandPacket.broadcastMessageSilently(record.getMessage(), permission);
|
||||
ProtocolLib.this.commandPacket.broadcastMessageSilently(record.getMessage(), permission);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,23 +298,19 @@ public class ProtocolLib extends JavaPlugin {
|
||||
}
|
||||
};
|
||||
|
||||
logger.addHandler(redirectHandler);
|
||||
logger.addHandler(this.redirectHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
try {
|
||||
Server server = getServer();
|
||||
Server server = this.getServer();
|
||||
PluginManager manager = server.getPluginManager();
|
||||
|
||||
// Don't do anything else!
|
||||
if (manager == null)
|
||||
return;
|
||||
|
||||
// Silly plugin reloaders!
|
||||
if (protocolManager == null) {
|
||||
Logger directLogging = Logger.getLogger("Minecraft");
|
||||
String[] message = new String[] {
|
||||
String[] message = new String[]{
|
||||
" ProtocolLib does not support plugin reloaders! ", " Please use the built-in reload command! "
|
||||
};
|
||||
|
||||
@@ -365,59 +319,48 @@ public class ProtocolLib extends JavaPlugin {
|
||||
directLogging.severe(line);
|
||||
}
|
||||
|
||||
disablePlugin();
|
||||
this.disablePlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for incompatible plugins
|
||||
checkForIncompatibility(manager);
|
||||
|
||||
// Initialize background compiler
|
||||
if (backgroundCompiler == null && config.isBackgroundCompilerEnabled()) {
|
||||
backgroundCompiler = new BackgroundCompiler(getClassLoader(), reporter);
|
||||
BackgroundCompiler.setInstance(backgroundCompiler);
|
||||
|
||||
logger.info("Started structure compiler thread.");
|
||||
} else {
|
||||
logger.info("Structure compiler thread has been disabled.");
|
||||
}
|
||||
this.checkForIncompatibility(manager);
|
||||
|
||||
// Set up command handlers
|
||||
registerCommand(CommandProtocol.NAME, commandProtocol);
|
||||
registerCommand(CommandPacket.NAME, commandPacket);
|
||||
registerCommand(CommandFilter.NAME, commandFilter);
|
||||
registerCommand(PacketLogging.NAME, packetLogging);
|
||||
this.registerCommand(CommandProtocol.NAME, this.commandProtocol);
|
||||
this.registerCommand(CommandPacket.NAME, this.commandPacket);
|
||||
this.registerCommand(CommandFilter.NAME, this.commandFilter);
|
||||
this.registerCommand(PacketLogging.NAME, this.packetLogging);
|
||||
|
||||
// Player login and logout events
|
||||
protocolManager.registerEvents(manager, this);
|
||||
|
||||
// Worker that ensures that async packets are eventually sent
|
||||
// It also performs the update check.
|
||||
createPacketTask(server);
|
||||
this.createPacketTask(server);
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_PLUGIN_ENABLE_ERROR).error(e));
|
||||
disablePlugin();
|
||||
this.disablePlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to enable statistics
|
||||
try {
|
||||
if (config.isMetricsEnabled()) {
|
||||
statistics = new Statistics(this);
|
||||
this.statistics = new Statistics(this);
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_METRICS_IO_ERROR).error(e).callerParam(statistics));
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_METRICS_IO_ERROR).error(e).callerParam(this.statistics));
|
||||
} catch (Throwable e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_METRICS_GENERIC_ERROR).error(e).callerParam(statistics));
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_METRICS_GENERIC_ERROR).error(e).callerParam(
|
||||
this.statistics));
|
||||
}
|
||||
}
|
||||
|
||||
// Plugin authors: Notify me to remove these
|
||||
|
||||
private void checkForIncompatibility(PluginManager manager) {
|
||||
for (String plugin : ProtocolLibrary.INCOMPATIBLE) {
|
||||
if (manager.getPlugin(plugin) != null) {
|
||||
@@ -434,26 +377,31 @@ public class ProtocolLib extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
// Plugin authors: Notify me to remove these
|
||||
|
||||
// Used to check Minecraft version
|
||||
private MinecraftVersion verifyMinecraftVersion() {
|
||||
MinecraftVersion minimum = new MinecraftVersion(ProtocolLibrary.MINIMUM_MINECRAFT_VERSION);
|
||||
MinecraftVersion maximum = new MinecraftVersion(ProtocolLibrary.MAXIMUM_MINECRAFT_VERSION);
|
||||
|
||||
try {
|
||||
MinecraftVersion current = new MinecraftVersion(getServer());
|
||||
MinecraftVersion current = new MinecraftVersion(this.getServer());
|
||||
|
||||
// Skip certain versions
|
||||
if (!config.getIgnoreVersionCheck().equals(current.getVersion())) {
|
||||
// We'll just warn the user for now
|
||||
if (current.compareTo(minimum) < 0)
|
||||
if (current.compareTo(minimum) < 0) {
|
||||
logger.warning("Version " + current + " is lower than the minimum " + minimum);
|
||||
if (current.compareTo(maximum) > 0)
|
||||
}
|
||||
if (current.compareTo(maximum) > 0) {
|
||||
logger.warning("Version " + current + " has not yet been tested! Proceed with caution.");
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
} catch (Exception e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_PARSE_MINECRAFT_VERSION).error(e).messageParam(maximum));
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_PARSE_MINECRAFT_VERSION).error(e).messageParam(maximum));
|
||||
|
||||
// Unknown version - just assume it is the latest
|
||||
return maximum;
|
||||
@@ -462,16 +410,16 @@ public class ProtocolLib extends JavaPlugin {
|
||||
|
||||
private void checkConflictingVersions() {
|
||||
Pattern ourPlugin = Pattern.compile("ProtocolLib-(.*)\\.jar");
|
||||
MinecraftVersion currentVersion = new MinecraftVersion(getDescription().getVersion());
|
||||
MinecraftVersion currentVersion = new MinecraftVersion(this.getDescription().getVersion());
|
||||
MinecraftVersion newestVersion = null;
|
||||
|
||||
// Skip the file that contains this current instance however
|
||||
File loadedFile = getFile();
|
||||
File loadedFile = this.getFile();
|
||||
|
||||
try {
|
||||
// Scan the plugin folder for newer versions of ProtocolLib
|
||||
// The plugin folder isn't always plugins/
|
||||
File pluginFolder = getDataFolder().getParentFile();
|
||||
File pluginFolder = this.getDataFolder().getParentFile();
|
||||
|
||||
File[] candidates = pluginFolder.listFiles();
|
||||
if (candidates != null) {
|
||||
@@ -499,7 +447,7 @@ public class ProtocolLib extends JavaPlugin {
|
||||
// See if the newest version is actually higher
|
||||
if (newestVersion != null && currentVersion.compareTo(newestVersion) < 0) {
|
||||
// We don't need to set internal classes or instances to NULL - that would break the other loaded plugin
|
||||
skipDisable = true;
|
||||
this.skipDisable = true;
|
||||
|
||||
throw new IllegalStateException(String.format(
|
||||
"Detected a newer version of ProtocolLib (%s) in plugin folder than the current (%s). Disabling.",
|
||||
@@ -510,10 +458,11 @@ public class ProtocolLib extends JavaPlugin {
|
||||
private void registerCommand(String name, CommandExecutor executor) {
|
||||
try {
|
||||
// Ignore these - they must have printed an error already
|
||||
if (executor == null)
|
||||
if (executor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PluginCommand command = getCommand(name);
|
||||
PluginCommand command = this.getCommand(name);
|
||||
|
||||
// Try to load the command
|
||||
if (command != null) {
|
||||
@@ -522,7 +471,8 @@ public class ProtocolLib extends JavaPlugin {
|
||||
throw new RuntimeException("plugin.yml might be corrupt.");
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_REGISTER_COMMAND).messageParam(name, e.getMessage()).error(e));
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_REGISTER_COMMAND).messageParam(name, e.getMessage()).error(e));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,44 +480,42 @@ public class ProtocolLib extends JavaPlugin {
|
||||
* Disable the current plugin.
|
||||
*/
|
||||
private void disablePlugin() {
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
this.getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
|
||||
private void createPacketTask(Server server) {
|
||||
try {
|
||||
if (packetTask >= 0)
|
||||
if (this.packetTask >= 0) {
|
||||
throw new IllegalStateException("Packet task has already been created");
|
||||
}
|
||||
|
||||
// Attempt to create task
|
||||
packetTask = server.getScheduler().scheduleSyncRepeatingTask(this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
AsyncFilterManager manager = (AsyncFilterManager) protocolManager.getAsynchronousManager();
|
||||
this.packetTask = server.getScheduler().scheduleSyncRepeatingTask(this, () -> {
|
||||
AsyncFilterManager manager = (AsyncFilterManager) protocolManager.getAsynchronousManager();
|
||||
|
||||
// We KNOW we're on the main thread at the moment
|
||||
manager.sendProcessedPackets(tickCounter++, true);
|
||||
// We KNOW we're on the main thread at the moment
|
||||
manager.sendProcessedPackets(ProtocolLib.this.tickCounter++, true);
|
||||
|
||||
// House keeping
|
||||
updateConfiguration();
|
||||
// House keeping
|
||||
ProtocolLib.this.updateConfiguration();
|
||||
|
||||
// Check for updates too
|
||||
if (!UPDATES_DISABLED && (tickCounter % 20) == 0) {
|
||||
checkUpdates();
|
||||
}
|
||||
// Check for updates too
|
||||
if (!ProtocolLibrary.updatesDisabled() && (ProtocolLib.this.tickCounter % 20) == 0) {
|
||||
ProtocolLib.this.checkUpdates();
|
||||
}
|
||||
}, ASYNC_MANAGER_DELAY, ASYNC_MANAGER_DELAY);
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
if (packetTask == -1) {
|
||||
if (this.packetTask == -1) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_CREATE_TIMEOUT_TASK).error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateConfiguration() {
|
||||
if (config != null && config.getModificationCount() != configExpectedMod) {
|
||||
configExpectedMod = config.getModificationCount();
|
||||
if (config != null && config.getModificationCount() != this.configExpectedMod) {
|
||||
this.configExpectedMod = config.getModificationCount();
|
||||
|
||||
// Update the debug flag
|
||||
protocolManager.setDebug(config.isDebug());
|
||||
@@ -577,58 +525,64 @@ public class ProtocolLib extends JavaPlugin {
|
||||
private void checkUpdates() {
|
||||
// Ignore milliseconds - it's pointless
|
||||
long currentTime = System.currentTimeMillis() / MILLI_PER_SECOND;
|
||||
|
||||
|
||||
try {
|
||||
long updateTime = config.getAutoLastTime() + config.getAutoDelay();
|
||||
|
||||
// Should we update?
|
||||
if (currentTime > updateTime && !updater.isChecking()) {
|
||||
if (currentTime > updateTime && !this.updater.isChecking()) {
|
||||
// Initiate the update as if it came from the console
|
||||
if (config.isAutoDownload())
|
||||
commandProtocol.updateVersion(getServer().getConsoleSender(), false);
|
||||
else if (config.isAutoNotify())
|
||||
commandProtocol.checkVersion(getServer().getConsoleSender(), false);
|
||||
else
|
||||
commandProtocol.updateFinished();
|
||||
if (config.isAutoDownload()) {
|
||||
this.commandProtocol.updateVersion(this.getServer().getConsoleSender(), false);
|
||||
} else if (config.isAutoNotify()) {
|
||||
this.commandProtocol.checkVersion(this.getServer().getConsoleSender(), false);
|
||||
} else {
|
||||
this.commandProtocol.updateFinished();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_UPDATE_PLUGIN).error(e));
|
||||
UPDATES_DISABLED = true;
|
||||
ProtocolLibrary.disableUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (skipDisable) {
|
||||
if (this.skipDisable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable compiler
|
||||
if (backgroundCompiler != null) {
|
||||
backgroundCompiler.shutdownAll();
|
||||
backgroundCompiler = null;
|
||||
BackgroundCompiler.setInstance(null);
|
||||
// that reloading the server might break ProtocolLib / plugins depending on it
|
||||
if (Util.isCurrentlyReloading()) {
|
||||
logger.severe("╔══════════════════════════════════════════════════════════════════╗");
|
||||
logger.severe("║ WARNING ║");
|
||||
logger.severe("║ RELOADING THE SERVER WHILE PROTOCOL LIB IS ENABLED MIGHT ║");
|
||||
logger.severe("║ LEAD TO UNEXPECTED ERRORS! ║");
|
||||
logger.severe("║ ║");
|
||||
logger.severe("║ Consider to cleanly restart your server if you encounter ║");
|
||||
logger.severe("║ any issues related to Protocol Lib before opening an issue ║");
|
||||
logger.severe("║ on GitHub! ║");
|
||||
logger.severe("╚══════════════════════════════════════════════════════════════════╝");
|
||||
}
|
||||
|
||||
// Clean up
|
||||
if (packetTask >= 0) {
|
||||
getServer().getScheduler().cancelTask(packetTask);
|
||||
packetTask = -1;
|
||||
if (this.packetTask >= 0) {
|
||||
this.getServer().getScheduler().cancelTask(this.packetTask);
|
||||
this.packetTask = -1;
|
||||
}
|
||||
|
||||
// And redirect handler too
|
||||
if (redirectHandler != null) {
|
||||
logger.removeHandler(redirectHandler);
|
||||
if (this.redirectHandler != null) {
|
||||
logger.removeHandler(this.redirectHandler);
|
||||
}
|
||||
if (protocolManager != null)
|
||||
if (protocolManager != null) {
|
||||
protocolManager.close();
|
||||
else
|
||||
} else {
|
||||
return; // Plugin reloaders!
|
||||
}
|
||||
|
||||
if (unhookTask != null)
|
||||
unhookTask.close();
|
||||
protocolManager = null;
|
||||
statistics = null;
|
||||
this.statistics = null;
|
||||
|
||||
// To clean up global parameters
|
||||
reporter = new BasicErrorReporter();
|
||||
@@ -637,15 +591,24 @@ public class ProtocolLib extends JavaPlugin {
|
||||
/**
|
||||
* Retrieve the metrics instance used to measure users of this library.
|
||||
* <p>
|
||||
* Note that this method may return NULL when the server is reloading or shutting down. It is also
|
||||
* NULL if metrics has been disabled.
|
||||
* Note that this method may return NULL when the server is reloading or shutting down. It is also NULL if metrics has
|
||||
* been disabled.
|
||||
*
|
||||
* @return Metrics instance container.
|
||||
*/
|
||||
public Statistics getStatistics() {
|
||||
return statistics;
|
||||
return this.statistics;
|
||||
}
|
||||
|
||||
public ProtocolConfig getProtocolConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
// Different commands
|
||||
private enum ProtocolCommand {
|
||||
FILTER,
|
||||
PACKET,
|
||||
PROTOCOL,
|
||||
LOGGING
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,24 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2016 dmulloy2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2016 dmulloy2
|
||||
* <p>
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
* <p>
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
* <p>
|
||||
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.comphenix.protocol.error.BasicErrorReporter;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import java.util.List;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@@ -30,6 +27,7 @@ import org.bukkit.plugin.Plugin;
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class ProtocolLibrary {
|
||||
|
||||
/**
|
||||
* The minimum version ProtocolLib has been tested with.
|
||||
*/
|
||||
@@ -38,17 +36,17 @@ public class ProtocolLibrary {
|
||||
/**
|
||||
* The maximum version ProtocolLib has been tested with.
|
||||
*/
|
||||
public static final String MAXIMUM_MINECRAFT_VERSION = "1.17.1";
|
||||
public static final String MAXIMUM_MINECRAFT_VERSION = MinecraftVersion.LATEST.getVersion();
|
||||
|
||||
/**
|
||||
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.17.1) was released.
|
||||
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.19.2) was released.
|
||||
*/
|
||||
public static final String MINECRAFT_LAST_RELEASE_DATE = "2021-07-06";
|
||||
public static final String MINECRAFT_LAST_RELEASE_DATE = "2022-08-05";
|
||||
|
||||
/**
|
||||
* Plugins that are currently incompatible with ProtocolLib.
|
||||
*/
|
||||
public static final List<String> INCOMPATIBLE = Arrays.asList("TagAPI");
|
||||
public static final List<String> INCOMPATIBLE = ImmutableList.of("TagAPI");
|
||||
|
||||
private static Plugin plugin;
|
||||
private static ProtocolConfig config;
|
||||
@@ -107,7 +105,7 @@ public class ProtocolLibrary {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not updates are currently disabled.
|
||||
* Whether updates are currently disabled.
|
||||
* @return True if it is, false if not
|
||||
*/
|
||||
public static boolean updatesDisabled() {
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* @author dmulloy2
|
||||
|
||||
@@ -17,16 +17,6 @@
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.async.AsyncMarker;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
@@ -36,81 +26,94 @@ import com.comphenix.protocol.injector.PacketConstructor;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents an API for accessing the Minecraft protocol.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface ProtocolManager extends PacketStream {
|
||||
|
||||
/**
|
||||
* Retrieve the protocol version of a given player.
|
||||
* <p>
|
||||
* This only really makes sense of a server that support clients of multiple Minecraft versions, such as Spigot #1628.
|
||||
* This only really makes sense of a server that support clients of multiple Minecraft versions, such as Spigot
|
||||
* #1628.
|
||||
*
|
||||
* @param player - the player.
|
||||
* @return The associated protocol version, or {@link Integer#MIN_VALUE} if unknown.
|
||||
*/
|
||||
int getProtocolVersion(Player player);
|
||||
|
||||
|
||||
/**
|
||||
* Send a packet to the given player.
|
||||
* <p>
|
||||
* Re-sending a previously cancelled packet is discouraged. Use {@link AsyncMarker#incrementProcessingDelay()}
|
||||
* to delay a packet until a certain condition has been met.
|
||||
*
|
||||
* Re-sending a previously cancelled packet is discouraged. Use {@link AsyncMarker#incrementProcessingDelay()} to
|
||||
* delay a packet until a certain condition has been met.
|
||||
*
|
||||
* @param receiver - the receiver.
|
||||
* @param packet - packet to send.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
* @throws InvocationTargetException - if an error occurred when sending the packet.
|
||||
* @param packet - packet to send.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
*/
|
||||
@Override
|
||||
void sendServerPacket(Player receiver, PacketContainer packet, boolean filters)
|
||||
throws InvocationTargetException;
|
||||
|
||||
void sendServerPacket(Player receiver, PacketContainer packet, boolean filters);
|
||||
|
||||
/**
|
||||
* Simulate receiving a certain packet from a given player.
|
||||
* <p>
|
||||
* Receiving a previously cancelled packet is discouraged. Use {@link AsyncMarker#incrementProcessingDelay()}
|
||||
* to delay a packet until a certain condition has been met.
|
||||
*
|
||||
* @param sender - the sender.
|
||||
* @param packet - the packet that was sent.
|
||||
* Receiving a previously cancelled packet is discouraged. Use {@link AsyncMarker#incrementProcessingDelay()} to delay
|
||||
* a packet until a certain condition has been met.
|
||||
*
|
||||
* @param sender - the sender.
|
||||
* @param packet - the packet that was sent.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
* @throws InvocationTargetException If the reflection machinery failed.
|
||||
* @throws IllegalAccessException If the underlying method caused an error.
|
||||
*/
|
||||
@Override
|
||||
void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
|
||||
throws IllegalAccessException, InvocationTargetException;
|
||||
|
||||
void receiveClientPacket(Player sender, PacketContainer packet, boolean filters);
|
||||
|
||||
/**
|
||||
* Broadcast a given packet to every connected player on the server.
|
||||
*
|
||||
* @param packet - the packet to broadcast.
|
||||
* @throws FieldAccessException If we were unable to send the packet due to reflection problems.
|
||||
*/
|
||||
void broadcastServerPacket(PacketContainer packet);
|
||||
|
||||
|
||||
/**
|
||||
* Broadcast a packet to every player that is receiving information about a given entity.
|
||||
* <p>
|
||||
* This is usually every player in the same world within an observable distance. If the entity is a
|
||||
* player, it will only be included if <i>includeTracker</i> is TRUE.
|
||||
* @param packet - the packet to broadcast.
|
||||
* @param entity - the entity whose trackers we will inform.
|
||||
* This is usually every player in the same world within an observable distance. If the entity is a player, it will
|
||||
* only be included if <i>includeTracker</i> is TRUE.
|
||||
*
|
||||
* @param packet - the packet to broadcast.
|
||||
* @param entity - the entity whose trackers we will inform.
|
||||
* @param includeTracker - whether or not to also transmit the packet to the entity, if it is a tracker.
|
||||
* @throws FieldAccessException If we were unable to send the packet due to reflection problems.
|
||||
*/
|
||||
void broadcastServerPacket(PacketContainer packet, Entity entity, boolean includeTracker);
|
||||
|
||||
|
||||
/**
|
||||
* Broadcast a packet to every player within the given maximum observer distance.
|
||||
* @param packet - the packet to broadcast.
|
||||
* @param origin - the origin to consider when calculating the distance to each observer.
|
||||
*
|
||||
* @param packet - the packet to broadcast.
|
||||
* @param origin - the origin to consider when calculating the distance to each observer.
|
||||
* @param maxObserverDistance - the maximum distance to the origin.
|
||||
*/
|
||||
void broadcastServerPacket(PacketContainer packet, Location origin, int maxObserverDistance);
|
||||
|
||||
|
||||
void broadcastServerPacket(PacketContainer packet, Collection<? extends Player> targetPlayers);
|
||||
|
||||
/**
|
||||
* Retrieves a list of every registered packet listener.
|
||||
*
|
||||
* @return Every registered packet listener.
|
||||
*/
|
||||
ImmutableSet<PacketListener> getPacketListeners();
|
||||
@@ -118,9 +121,9 @@ public interface ProtocolManager extends PacketStream {
|
||||
/**
|
||||
* Adds a packet listener.
|
||||
* <p>
|
||||
* Adding an already registered listener has no effect. If you need to change the packets
|
||||
* the current listener is observing, you must first remove the packet listener before you
|
||||
* can register it again.
|
||||
* Adding an already registered listener has no effect. If you need to change the packets the current listener is
|
||||
* observing, you must first remove the packet listener before you can register it again.
|
||||
*
|
||||
* @param listener - new packet listener.
|
||||
*/
|
||||
void addPacketListener(PacketListener listener);
|
||||
@@ -129,98 +132,109 @@ public interface ProtocolManager extends PacketStream {
|
||||
* Removes a given packet listener.
|
||||
* <p>
|
||||
* Attempting to remove a listener that doesn't exist has no effect.
|
||||
*
|
||||
* @param listener - the packet listener to remove.
|
||||
*/
|
||||
void removePacketListener(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Removes every listener associated with the given plugin.
|
||||
*
|
||||
* @param plugin - the plugin to unload.
|
||||
*/
|
||||
void removePacketListeners(Plugin plugin);
|
||||
|
||||
/**
|
||||
* Constructs a new encapsulated Minecraft packet with the given ID.
|
||||
* @param type - packet type.
|
||||
*
|
||||
* @param type - packet type.
|
||||
* @return New encapsulated Minecraft packet.
|
||||
*/
|
||||
PacketContainer createPacket(PacketType type);
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new encapsulated Minecraft packet with the given ID.
|
||||
* <p>
|
||||
* If set to true, the <i>forceDefaults</i> option will force the system to automatically
|
||||
* give non-primitive fields in the packet sensible default values. For instance, certain
|
||||
* packets - like Packet60Explosion - require a List or Set to be non-null. If the
|
||||
* forceDefaults option is true, the List or Set will be automatically created.
|
||||
*
|
||||
* @param type - packet type.
|
||||
* If set to true, the <i>forceDefaults</i> option will force the system to automatically give non-primitive fields in
|
||||
* the packet sensible default values. For instance, certain packets - like Packet60Explosion - require a List or Set
|
||||
* to be non-null. If the forceDefaults option is true, the List or Set will be automatically created.
|
||||
*
|
||||
* @param type - packet type.
|
||||
* @param forceDefaults - TRUE to use sensible defaults in most fields, FALSE otherwise.
|
||||
* @return New encapsulated Minecraft packet.
|
||||
*/
|
||||
PacketContainer createPacket(PacketType type, boolean forceDefaults);
|
||||
|
||||
|
||||
/**
|
||||
* Construct a packet using the special builtin Minecraft constructors.
|
||||
* @param type - the packet type.
|
||||
*
|
||||
* @param type - the packet type.
|
||||
* @param arguments - arguments that will be passed to the constructor.
|
||||
* @return The packet constructor.
|
||||
*/
|
||||
PacketConstructor createPacketConstructor(PacketType type, Object... arguments);
|
||||
|
||||
|
||||
/**
|
||||
* Completely resend an entity to a list of clients.
|
||||
* <p>
|
||||
* Note that this method is NOT thread safe. If you call this method from anything
|
||||
* but the main thread, it will throw an exception.
|
||||
* @param entity - entity to refresh.
|
||||
* Note that this method is NOT thread safe. If you call this method from anything but the main thread, it will throw
|
||||
* an exception.
|
||||
*
|
||||
* @param entity - entity to refresh.
|
||||
* @param observers - the clients to update.
|
||||
*/
|
||||
void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException;
|
||||
|
||||
void updateEntity(Entity entity, List<Player> observers);
|
||||
|
||||
/**
|
||||
* Retrieve the associated entity.
|
||||
*
|
||||
* @param container - the world the entity belongs to.
|
||||
* @param id - the unique ID of the entity.
|
||||
* @param id - the unique ID of the entity.
|
||||
* @return The associated entity.
|
||||
* @throws FieldAccessException Reflection failed.
|
||||
*/
|
||||
Entity getEntityFromID(World container, int id) throws FieldAccessException;
|
||||
|
||||
Entity getEntityFromID(World container, int id);
|
||||
|
||||
/**
|
||||
* Retrieve every client that is receiving information about a given entity.
|
||||
*
|
||||
* @param entity - the entity that is being tracked.
|
||||
* @return Every client/player that is tracking the given entity.
|
||||
* @throws FieldAccessException If reflection failed.
|
||||
*/
|
||||
List<Player> getEntityTrackers(Entity entity) throws FieldAccessException;
|
||||
List<Player> getEntityTrackers(Entity entity);
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the type of the sent server packets that will be observed by listeners.
|
||||
*
|
||||
* @return Every filtered server packet.
|
||||
*/
|
||||
Set<PacketType> getSendingFilterTypes();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the type of the received client packets that will be observed by listeners.
|
||||
*
|
||||
* @return Every filtered client packet.
|
||||
*/
|
||||
Set<PacketType> getReceivingFilterTypes();
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the current Minecraft version.
|
||||
*
|
||||
* @return The current version.
|
||||
*/
|
||||
MinecraftVersion getMinecraftVersion();
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether or not this protocol manager has been disabled.
|
||||
*
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
boolean isClosed();
|
||||
|
||||
/**
|
||||
* Retrieve the current asynchronous packet manager.
|
||||
*
|
||||
* @return Asynchronous packet manager.
|
||||
*/
|
||||
AsynchronousManager getAsynchronousManager();
|
||||
|
||||
@@ -17,22 +17,24 @@
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ContiguousSet;
|
||||
import com.google.common.collect.DiscreteDomain;
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Used to parse ranges in CommandPacket.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class RangeParser {
|
||||
final class RangeParser {
|
||||
private RangeParser() {}
|
||||
|
||||
/**
|
||||
* Parse a range from a given text.
|
||||
* @param text - the text.
|
||||
@@ -40,20 +42,18 @@ class RangeParser {
|
||||
* @return The parsed ranges.
|
||||
*/
|
||||
public static List<Range<Integer>> getRanges(String text, Range<Integer> legalRange) {
|
||||
return getRanges(new ArrayDeque<String>(Arrays.asList(text)), legalRange);
|
||||
return getRanges(new ArrayDeque<>(Collections.singletonList(text)), legalRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse ranges from an array of elements.
|
||||
* @param args - array of elements.
|
||||
* @param offset - beginning offset.
|
||||
* @param lastIndex - the last index of the array to read.
|
||||
* @param input the input to parse the ranges from
|
||||
* @param legalRange - range of legal values.
|
||||
* @return The parsed ranges.
|
||||
*/
|
||||
public static List<Range<Integer>> getRanges(Deque<String> input, Range<Integer> legalRange) {
|
||||
List<String> tokens = tokenizeInput(input);
|
||||
List<Range<Integer>> ranges = new ArrayList<Range<Integer>>();
|
||||
List<Range<Integer>> ranges = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < tokens.size(); i++) {
|
||||
Range<Integer> range;
|
||||
@@ -96,7 +96,7 @@ class RangeParser {
|
||||
* @return A simplified list of ranges.
|
||||
*/
|
||||
private static List<Range<Integer>> simplify(List<Range<Integer>> ranges, int maximum) {
|
||||
List<Range<Integer>> result = new ArrayList<Range<Integer>>();
|
||||
List<Range<Integer>> result = new ArrayList<>();
|
||||
boolean[] set = new boolean[maximum + 1];
|
||||
int start = -1;
|
||||
|
||||
@@ -125,7 +125,7 @@ class RangeParser {
|
||||
}
|
||||
|
||||
private static List<String> tokenizeInput(Deque<String> input) {
|
||||
List<String> tokens = new ArrayList<String>();
|
||||
List<String> tokens = new ArrayList<>();
|
||||
|
||||
// Tokenize the input
|
||||
while (!input.isEmpty()) {
|
||||
|
||||
@@ -36,7 +36,6 @@ import com.comphenix.protocol.injector.SortedPacketListenerList;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
@@ -87,7 +86,7 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
// Initialize timeout listeners
|
||||
this.serverTimeoutListeners = new SortedPacketListenerList();
|
||||
this.clientTimeoutListeners = new SortedPacketListenerList();
|
||||
this.timeoutListeners = Sets.newSetFromMap(new ConcurrentHashMap<>());
|
||||
this.timeoutListeners = ConcurrentHashMap.newKeySet();
|
||||
|
||||
this.playerSendingHandler = new PlayerSendingHandler(reporter, serverTimeoutListeners, clientTimeoutListeners);
|
||||
this.serverProcessingQueue = new PacketProcessingQueue(playerSendingHandler);
|
||||
|
||||
@@ -121,16 +121,10 @@ public class AsyncListenerHandler {
|
||||
}
|
||||
|
||||
private void startWarningTask() {
|
||||
warningTask = filterManager.getScheduler().scheduleSyncDelayedTask(getPlugin(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ProtocolLibrary.getErrorReporter().reportWarning(AsyncListenerHandler.this, Report.
|
||||
newBuilder(REPORT_HANDLER_NOT_STARTED).
|
||||
messageParam(listener.getPlugin(), AsyncListenerHandler.this).
|
||||
build()
|
||||
);
|
||||
}
|
||||
}, 2 * TICKS_PER_SECOND);
|
||||
warningTask = filterManager.getScheduler().scheduleSyncDelayedTask(getPlugin(), () -> ProtocolLibrary.getErrorReporter().reportWarning(AsyncListenerHandler.this, Report.
|
||||
newBuilder(REPORT_HANDLER_NOT_STARTED).
|
||||
messageParam(listener.getPlugin(), AsyncListenerHandler.this).
|
||||
build()), 2 * TICKS_PER_SECOND);
|
||||
}
|
||||
|
||||
private void stopWarningTask() {
|
||||
@@ -287,19 +281,16 @@ public class AsyncListenerHandler {
|
||||
final AsyncRunnable listenerLoop = getListenerLoop();
|
||||
|
||||
stopWarningTask();
|
||||
scheduleAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Thread thread = Thread.currentThread();
|
||||
|
||||
String previousName = thread.getName();
|
||||
String workerName = getFriendlyWorkerName(listenerLoop.getID());
|
||||
scheduleAsync(() -> {
|
||||
Thread thread = Thread.currentThread();
|
||||
|
||||
// Add the friendly worker name
|
||||
thread.setName(workerName);
|
||||
listenerLoop.run();
|
||||
thread.setName(previousName);
|
||||
}
|
||||
String previousName = thread.getName();
|
||||
String workerName = getFriendlyWorkerName(listenerLoop.getID());
|
||||
|
||||
// Add the friendly worker name
|
||||
thread.setName(workerName);
|
||||
listenerLoop.run();
|
||||
thread.setName(previousName);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -335,12 +326,7 @@ public class AsyncListenerHandler {
|
||||
final AsyncRunnable listenerLoop = getListenerLoop();
|
||||
final Function<AsyncRunnable, Void> delegateCopy = executor;
|
||||
|
||||
scheduleAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delegateCopy.apply(listenerLoop);
|
||||
}
|
||||
});
|
||||
scheduleAsync(() -> delegateCopy.apply(listenerLoop));
|
||||
}
|
||||
|
||||
private void scheduleAsync(Runnable runnable) {
|
||||
@@ -423,31 +409,28 @@ public class AsyncListenerHandler {
|
||||
if (syncTask < 0) {
|
||||
stopWarningTask();
|
||||
|
||||
syncTask = filterManager.getScheduler().scheduleSyncRepeatingTask(getPlugin(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
long stopTime = System.nanoTime() + unit.convert(time, TimeUnit.NANOSECONDS);
|
||||
|
||||
while (!cancelled) {
|
||||
PacketEvent packet = queuedPackets.poll();
|
||||
|
||||
if (packet == INTERUPT_PACKET || packet == WAKEUP_PACKET) {
|
||||
// Sorry, asynchronous threads!
|
||||
queuedPackets.add(packet);
|
||||
|
||||
// Try again next tick
|
||||
break;
|
||||
} else if (packet != null && packet.getAsyncMarker() != null) {
|
||||
processPacket(workerID, packet, "onSyncPacket()");
|
||||
} else {
|
||||
// No more packets left - wait a tick
|
||||
break;
|
||||
}
|
||||
|
||||
// Check time here, ensuring that we at least process one packet
|
||||
if (System.nanoTime() < stopTime)
|
||||
break;
|
||||
syncTask = filterManager.getScheduler().scheduleSyncRepeatingTask(getPlugin(), () -> {
|
||||
long stopTime = System.nanoTime() + unit.convert(time, TimeUnit.NANOSECONDS);
|
||||
|
||||
while (!cancelled) {
|
||||
PacketEvent packet = queuedPackets.poll();
|
||||
|
||||
if (packet == INTERUPT_PACKET || packet == WAKEUP_PACKET) {
|
||||
// Sorry, asynchronous threads!
|
||||
queuedPackets.add(packet);
|
||||
|
||||
// Try again next tick
|
||||
break;
|
||||
} else if (packet != null && packet.getAsyncMarker() != null) {
|
||||
processPacket(workerID, packet, "onSyncPacket()");
|
||||
} else {
|
||||
// No more packets left - wait a tick
|
||||
break;
|
||||
}
|
||||
|
||||
// Check time here, ensuring that we at least process one packet
|
||||
if (System.nanoTime() < stopTime)
|
||||
break;
|
||||
}
|
||||
}, tickDelay, tickDelay);
|
||||
|
||||
|
||||
@@ -42,11 +42,11 @@ import com.google.common.primitives.Longs;
|
||||
* Contains information about the packet that is being processed by asynchronous listeners.
|
||||
* <p>
|
||||
* Asynchronous listeners can use this to set packet timeout or transmission order.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
|
||||
|
||||
/**
|
||||
* Generated by Eclipse.
|
||||
*/
|
||||
@@ -56,52 +56,52 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
* Default number of milliseconds until a packet will rejected.
|
||||
*/
|
||||
public static final int DEFAULT_TIMEOUT_DELTA = 1800 * 1000;
|
||||
|
||||
|
||||
/**
|
||||
* Default number of packets to skip.
|
||||
*/
|
||||
public static final int DEFAULT_SENDING_DELTA = 0;
|
||||
|
||||
|
||||
/**
|
||||
* The packet stream responsible for transmitting the packet when it's done processing.
|
||||
*/
|
||||
private transient PacketStream packetStream;
|
||||
|
||||
|
||||
/**
|
||||
* Current list of async packet listeners.
|
||||
*/
|
||||
private transient Iterator<PrioritizedListener<AsyncListenerHandler>> listenerTraversal;
|
||||
|
||||
|
||||
// Timeout handling
|
||||
private long initialTime;
|
||||
private long timeout;
|
||||
|
||||
|
||||
// Packet order
|
||||
private long originalSendingIndex;
|
||||
private long newSendingIndex;
|
||||
|
||||
|
||||
// Used to determine if a packet must be reordered in the sending queue
|
||||
private Long queuedSendingIndex;
|
||||
|
||||
|
||||
// Whether or not the packet has been processed by the listeners
|
||||
private volatile boolean processed;
|
||||
|
||||
|
||||
// Whether or not the packet has been sent
|
||||
private volatile boolean transmitted;
|
||||
|
||||
|
||||
// Whether or not the asynchronous processing itself should be cancelled
|
||||
private volatile boolean asyncCancelled;
|
||||
|
||||
|
||||
// Whether or not to delay processing
|
||||
private AtomicInteger processingDelay = new AtomicInteger();
|
||||
|
||||
|
||||
// Used to synchronize processing on the shared PacketEvent
|
||||
private Object processingLock = new Object();
|
||||
|
||||
|
||||
// Used to identify the asynchronous worker
|
||||
private transient AsyncListenerHandler listenerHandler;
|
||||
private transient int workerID;
|
||||
|
||||
|
||||
// Determine if Minecraft processes this packet asynchronously
|
||||
private volatile static Method isMinecraftAsync;
|
||||
private volatile static boolean alwaysSync;
|
||||
@@ -113,18 +113,18 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
AsyncMarker(PacketStream packetStream, long sendingIndex, long initialTime, long timeoutDelta) {
|
||||
if (packetStream == null)
|
||||
throw new IllegalArgumentException("packetStream cannot be NULL");
|
||||
|
||||
|
||||
this.packetStream = packetStream;
|
||||
|
||||
|
||||
// Timeout
|
||||
this.initialTime = initialTime;
|
||||
this.timeout = initialTime + timeoutDelta;
|
||||
|
||||
|
||||
// Sending index
|
||||
this.originalSendingIndex = sendingIndex;
|
||||
this.newSendingIndex = sendingIndex;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the time the packet was initially queued for asynchronous processing.
|
||||
* @return The initial time in number of milliseconds since 01.01.1970 00:00.
|
||||
@@ -140,7 +140,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
public long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the time the packet will be forcefully rejected.
|
||||
* @param timeout - time to reject the packet, in milliseconds since 01.01.1970 00:00.
|
||||
@@ -219,13 +219,13 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
* <p>
|
||||
* It is recommended that processing outside a packet listener is wrapped in a synchronized block
|
||||
* using the {@link #getProcessingLock()} method.
|
||||
*
|
||||
*
|
||||
* @return The new processing delay.
|
||||
*/
|
||||
public int incrementProcessingDelay() {
|
||||
return processingDelay.incrementAndGet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decrement the number of times this packet must be signalled as done before it's transmitted.
|
||||
* @return The new processing delay. If zero, the packet should be sent.
|
||||
@@ -233,7 +233,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
int decrementProcessingDelay() {
|
||||
return processingDelay.decrementAndGet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the number of times a packet must be signalled to be done before it's sent.
|
||||
* @return Number of processing delays.
|
||||
@@ -241,7 +241,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
public int getProcessingDelay() {
|
||||
return processingDelay.get();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether or not this packet is or has been queued for processing.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
@@ -296,7 +296,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
public boolean hasExpired() {
|
||||
return hasExpired(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if this packet has expired given this time.
|
||||
* @param currentTime - the current time in milliseconds since 01.01.1970 00:00.
|
||||
@@ -305,7 +305,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
public boolean hasExpired(long currentTime) {
|
||||
return timeout < currentTime;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the asynchronous handling should be cancelled.
|
||||
* @return TRUE if it should, FALSE otherwise.
|
||||
@@ -319,7 +319,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
* <p>
|
||||
* This is only relevant during the synchronous processing. Asynchronous
|
||||
* listeners should use the normal cancel-field to cancel a PacketEvent.
|
||||
*
|
||||
*
|
||||
* @param asyncCancelled - TRUE to cancel it, FALSE otherwise.
|
||||
*/
|
||||
public void setAsyncCancelled(boolean asyncCancelled) {
|
||||
@@ -369,7 +369,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
Iterator<PrioritizedListener<AsyncListenerHandler>> getListenerTraversal() {
|
||||
return listenerTraversal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the iterator for the next listener.
|
||||
* @param listenerTraversal - the new async packet listener iterator.
|
||||
@@ -377,28 +377,22 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
void setListenerTraversal(Iterator<PrioritizedListener<AsyncListenerHandler>> listenerTraversal) {
|
||||
this.listenerTraversal = listenerTraversal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transmit a given packet to the current packet stream.
|
||||
* @param event - the packet to send.
|
||||
* @throws IOException If the packet couldn't be sent.
|
||||
*/
|
||||
void sendPacket(PacketEvent event) throws IOException {
|
||||
try {
|
||||
if (event.isServerPacket()) {
|
||||
packetStream.sendServerPacket(event.getPlayer(), event.getPacket(), NetworkMarker.getNetworkMarker(event), false);
|
||||
} else {
|
||||
packetStream.recieveClientPacket(event.getPlayer(), event.getPacket(), NetworkMarker.getNetworkMarker(event), false);
|
||||
}
|
||||
transmitted = true;
|
||||
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IOException("Cannot send packet", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IOException("Cannot send packet", e);
|
||||
if (event.isServerPacket()) {
|
||||
packetStream.sendServerPacket(event.getPlayer(), event.getPacket(), NetworkMarker.getNetworkMarker(event), false);
|
||||
} else {
|
||||
packetStream.receiveClientPacket(event.getPlayer(), event.getPacket(), NetworkMarker.getNetworkMarker(event),
|
||||
false);
|
||||
}
|
||||
transmitted = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if Minecraft allows asynchronous processing of this packet.
|
||||
* @param event - packet event
|
||||
@@ -413,7 +407,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
// This will occur in 1.2.5 (or possibly in later versions)
|
||||
List<Method> methods = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
|
||||
getMethodListByParameters(boolean.class, new Class[] {});
|
||||
|
||||
|
||||
// Try to look for boolean methods
|
||||
if (methods.size() == 2) {
|
||||
isMinecraftAsync = methods.get(1);
|
||||
@@ -460,7 +454,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(AsyncMarker o) {
|
||||
if (o == null)
|
||||
@@ -468,7 +462,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
else
|
||||
return Longs.compare(getNewSendingIndex(), o.getNewSendingIndex());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
// Standard equals
|
||||
@@ -479,7 +473,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Longs.hashCode(getNewSendingIndex());
|
||||
|
||||
@@ -17,13 +17,9 @@
|
||||
|
||||
package com.comphenix.protocol.async;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import com.comphenix.protocol.events.*;
|
||||
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents a NO OPERATION listener.
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
* 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
|
||||
*/
|
||||
|
||||
@@ -24,81 +24,79 @@ import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents packets ready to be transmitted to a client.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
abstract class PacketSendingQueue {
|
||||
public static final ReportType REPORT_DROPPED_PACKET = new ReportType("Warning: Dropped packet index %s of type %s.");
|
||||
|
||||
|
||||
public static final int INITIAL_CAPACITY = 10;
|
||||
|
||||
private PriorityBlockingQueue<PacketEventHolder> sendingQueue;
|
||||
|
||||
// Asynchronous packet sending
|
||||
private Executor asynchronousSender;
|
||||
// Whether or not packet transmission must occur on a specific thread
|
||||
private final boolean notThreadSafe;
|
||||
private final PriorityBlockingQueue<PacketEventHolder> sendingQueue;
|
||||
// Asynchronous packet sending
|
||||
private final Executor asynchronousSender;
|
||||
// Whether or not we've run the cleanup procedure
|
||||
private boolean cleanedUp = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create a packet sending queue.
|
||||
*
|
||||
* @param notThreadSafe - whether or not to synchronize with the main thread or a background thread.
|
||||
*/
|
||||
public PacketSendingQueue(boolean notThreadSafe, Executor asynchronousSender) {
|
||||
this.sendingQueue = new PriorityBlockingQueue<PacketEventHolder>(INITIAL_CAPACITY);
|
||||
this.sendingQueue = new PriorityBlockingQueue<>(INITIAL_CAPACITY);
|
||||
this.notThreadSafe = notThreadSafe;
|
||||
this.asynchronousSender = asynchronousSender;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Number of packet events in the queue.
|
||||
*
|
||||
* @return The number of packet events in the queue.
|
||||
*/
|
||||
public int size() {
|
||||
return sendingQueue.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enqueue a packet for sending.
|
||||
* Enqueue a packet for sending.
|
||||
*
|
||||
* @param packet - packet to queue.
|
||||
*/
|
||||
public void enqueue(PacketEvent packet) {
|
||||
sendingQueue.add(new PacketEventHolder(packet));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoked when one of the packets have finished processing.
|
||||
*
|
||||
* @param packetUpdated - the packet that has now been updated.
|
||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||
*/
|
||||
public synchronized void signalPacketUpdate(PacketEvent packetUpdated, boolean onMainThread) {
|
||||
|
||||
|
||||
AsyncMarker marker = packetUpdated.getAsyncMarker();
|
||||
|
||||
|
||||
// Should we reorder the event?
|
||||
if (marker.getQueuedSendingIndex() != marker.getNewSendingIndex() && !marker.hasExpired()) {
|
||||
PacketEvent copy = PacketEvent.fromSynchronous(packetUpdated, marker);
|
||||
|
||||
|
||||
// "Cancel" the original event
|
||||
packetUpdated.setReadOnly(false);
|
||||
packetUpdated.setCancelled(true);
|
||||
|
||||
|
||||
// Enqueue the copy with the new sending index
|
||||
enqueue(copy);
|
||||
}
|
||||
|
||||
|
||||
// Mark this packet as finished
|
||||
marker.setProcessed(true);
|
||||
trySendPackets(onMainThread);
|
||||
@@ -107,83 +105,85 @@ abstract class PacketSendingQueue {
|
||||
/***
|
||||
* Invoked when a list of packet IDs are no longer associated with any listeners.
|
||||
* @param packetsRemoved - packets that no longer have any listeners.
|
||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||
* @param onMainThread - whether or not this is occurring on the main thread.
|
||||
*/
|
||||
public synchronized void signalPacketUpdate(List<PacketType> packetsRemoved, boolean onMainThread) {
|
||||
Set<PacketType> lookup = new HashSet<PacketType>(packetsRemoved);
|
||||
|
||||
Set<PacketType> lookup = new HashSet<>(packetsRemoved);
|
||||
|
||||
// Note that this is O(n), so it might be expensive
|
||||
for (PacketEventHolder holder : sendingQueue) {
|
||||
PacketEvent event = holder.getEvent();
|
||||
|
||||
|
||||
if (lookup.contains(event.getPacketType())) {
|
||||
event.getAsyncMarker().setProcessed(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is likely to have changed the situation a bit
|
||||
trySendPackets(onMainThread);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempt to send any remaining packets.
|
||||
*
|
||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||
*/
|
||||
public void trySendPackets(boolean onMainThread) {
|
||||
// Whether or not to continue sending packets
|
||||
boolean sending = true;
|
||||
|
||||
|
||||
// Transmit as many packets as we can
|
||||
while (sending) {
|
||||
PacketEventHolder holder = sendingQueue.poll();
|
||||
|
||||
|
||||
if (holder != null) {
|
||||
sending = processPacketHolder(onMainThread, holder);
|
||||
|
||||
|
||||
if (!sending) {
|
||||
// Add it back again
|
||||
sendingQueue.add(holder);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// No more packets to send
|
||||
sending = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoked when a packet might be ready for transmission.
|
||||
*
|
||||
* @param onMainThread - TRUE if we're on the main thread, FALSE otherwise.
|
||||
* @param holder - packet container.
|
||||
* @param holder - packet container.
|
||||
* @return TRUE to continue sending packets, FALSE otherwise.
|
||||
*/
|
||||
private boolean processPacketHolder(boolean onMainThread, final PacketEventHolder holder) {
|
||||
PacketEvent current = holder.getEvent();
|
||||
AsyncMarker marker = current.getAsyncMarker();
|
||||
boolean hasExpired = marker.hasExpired();
|
||||
|
||||
|
||||
// Guard in cause the queue is closed
|
||||
if (cleanedUp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// End condition?
|
||||
if (marker.isProcessed() || hasExpired) {
|
||||
if (hasExpired) {
|
||||
// Notify timeout listeners
|
||||
onPacketTimeout(current);
|
||||
|
||||
|
||||
// Recompute
|
||||
marker = current.getAsyncMarker();
|
||||
hasExpired = marker.hasExpired();
|
||||
|
||||
|
||||
// Could happen due to the timeout listeners
|
||||
if (!marker.isProcessed() && !hasExpired) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Is it okay to send the packet?
|
||||
if (!current.isCancelled() && !hasExpired) {
|
||||
// Make sure we're on the main thread
|
||||
@@ -191,65 +191,61 @@ abstract class PacketSendingQueue {
|
||||
try {
|
||||
boolean wantAsync = marker.isMinecraftAsync(current);
|
||||
boolean wantSync = !wantAsync;
|
||||
|
||||
|
||||
// Wait for the next main thread heartbeat if we haven't fulfilled our promise
|
||||
if (!onMainThread && wantSync) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Let's give it what it wants
|
||||
if (onMainThread && wantAsync) {
|
||||
asynchronousSender.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// We know this isn't on the main thread
|
||||
processPacketHolder(false, holder);
|
||||
}
|
||||
});
|
||||
|
||||
// We know this isn't on the main thread
|
||||
asynchronousSender.execute(() -> processPacketHolder(false, holder));
|
||||
|
||||
// Scheduler will do the rest
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} catch (FieldAccessException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
|
||||
// Just drop the packet
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Silently skip players that have logged out
|
||||
if (isOnline(current.getPlayer())) {
|
||||
sendPacket(current);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Drop the packet
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Add it back and stop sending
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoked when a packet has timed out.
|
||||
*
|
||||
* @param event - the timed out packet.
|
||||
*/
|
||||
protected abstract void onPacketTimeout(PacketEvent event);
|
||||
|
||||
|
||||
private boolean isOnline(Player player) {
|
||||
return player != null && player.isOnline();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send every packet, regardless of the processing state.
|
||||
*/
|
||||
private void forceSend() {
|
||||
while (true) {
|
||||
PacketEventHolder holder = sendingQueue.poll();
|
||||
|
||||
|
||||
if (holder != null) {
|
||||
sendPacket(holder.getEvent());
|
||||
} else {
|
||||
@@ -257,9 +253,10 @@ abstract class PacketSendingQueue {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether or not the packet transmission must synchronize with the main thread.
|
||||
*
|
||||
* @return TRUE if it must, FALSE otherwise.
|
||||
*/
|
||||
public boolean isSynchronizeMain() {
|
||||
@@ -268,24 +265,16 @@ abstract class PacketSendingQueue {
|
||||
|
||||
/**
|
||||
* Transmit a packet, if it hasn't already.
|
||||
*
|
||||
* @param event - the packet to transmit.
|
||||
*/
|
||||
private void sendPacket(PacketEvent event) {
|
||||
|
||||
AsyncMarker marker = event.getAsyncMarker();
|
||||
|
||||
try {
|
||||
// Don't send a packet twice
|
||||
AsyncMarker marker = event.getAsyncMarker();
|
||||
if (marker != null && !marker.isTransmitted()) {
|
||||
marker.sendPacket(event);
|
||||
}
|
||||
|
||||
} catch (PlayerLoggedOutException e) {
|
||||
ProtocolLibrary.getErrorReporter().reportDebug(this, Report.newBuilder(REPORT_DROPPED_PACKET).
|
||||
messageParam(marker.getOriginalSendingIndex(), event.getPacketType()).
|
||||
callerParam(event)
|
||||
);
|
||||
|
||||
} catch (IOException e) {
|
||||
// Just print the error
|
||||
e.printStackTrace();
|
||||
@@ -299,7 +288,7 @@ abstract class PacketSendingQueue {
|
||||
if (!cleanedUp) {
|
||||
// Note that the cleanup itself will always occur on the main thread
|
||||
forceSend();
|
||||
|
||||
|
||||
// And we're done
|
||||
cleanedUp = true;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.concurrency.ConcurrentPlayerMap;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
@@ -33,18 +31,20 @@ import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.SortedPacketListenerList;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Contains every sending queue for every player.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class PlayerSendingHandler {
|
||||
private ErrorReporter reporter;
|
||||
private ConcurrentMap<Player, QueueContainer> playerSendingQueues;
|
||||
private final ErrorReporter reporter;
|
||||
private final ConcurrentMap<Player, QueueContainer> playerSendingQueues;
|
||||
|
||||
// Timeout listeners
|
||||
private SortedPacketListenerList serverTimeoutListeners;
|
||||
private SortedPacketListenerList clientTimeoutListeners;
|
||||
private final SortedPacketListenerList serverTimeoutListeners;
|
||||
private final SortedPacketListenerList clientTimeoutListeners;
|
||||
|
||||
// Asynchronous packet sending
|
||||
private Executor asynchronousSender;
|
||||
@@ -58,8 +58,8 @@ class PlayerSendingHandler {
|
||||
* @author Kristian
|
||||
*/
|
||||
private class QueueContainer {
|
||||
private PacketSendingQueue serverQueue;
|
||||
private PacketSendingQueue clientQueue;
|
||||
private final PacketSendingQueue serverQueue;
|
||||
private final PacketSendingQueue clientQueue;
|
||||
|
||||
public QueueContainer() {
|
||||
// Server packets can be sent concurrently
|
||||
@@ -174,7 +174,7 @@ class PlayerSendingHandler {
|
||||
/**
|
||||
* Immediately send every server packet with the given list of IDs.
|
||||
* @param types - types of every packet to send immediately.
|
||||
* @param synchronusOK - whether or not we're running on the main thread.
|
||||
* @param synchronusOK - whether we're running on the main thread.
|
||||
*/
|
||||
public void sendServerPackets(List<PacketType> types, boolean synchronusOK) {
|
||||
if (!cleaningUp) {
|
||||
@@ -186,8 +186,7 @@ class PlayerSendingHandler {
|
||||
|
||||
/**
|
||||
* Immediately send every client packet with the given list of IDs.
|
||||
* @param ids - ID of every packet to send immediately.
|
||||
* @param synchronusOK - whether or not we're running on the main thread.
|
||||
* @param synchronusOK - whether we're running on the main thread.
|
||||
*/
|
||||
public void sendClientPackets(List<PacketType> types, boolean synchronusOK) {
|
||||
if (!cleaningUp) {
|
||||
@@ -199,7 +198,7 @@ class PlayerSendingHandler {
|
||||
|
||||
/**
|
||||
* Send any outstanding server packets.
|
||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||
* @param onMainThread - whether this is occurring on the main thread.
|
||||
*/
|
||||
public void trySendServerPackets(boolean onMainThread) {
|
||||
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||
@@ -209,7 +208,7 @@ class PlayerSendingHandler {
|
||||
|
||||
/**
|
||||
* Send any outstanding server packets.
|
||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||
* @param onMainThread - whether this is occurring on the main thread.
|
||||
*/
|
||||
public void trySendClientPackets(boolean onMainThread) {
|
||||
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||
@@ -222,7 +221,7 @@ class PlayerSendingHandler {
|
||||
* @return Every sever packet queue.
|
||||
*/
|
||||
public List<PacketSendingQueue> getServerQueues() {
|
||||
List<PacketSendingQueue> result = new ArrayList<PacketSendingQueue>();
|
||||
List<PacketSendingQueue> result = new ArrayList<>();
|
||||
|
||||
for (QueueContainer queue : playerSendingQueues.values())
|
||||
result.add(queue.getServerQueue());
|
||||
@@ -234,7 +233,7 @@ class PlayerSendingHandler {
|
||||
* @return Every client packet queue.
|
||||
*/
|
||||
public List<PacketSendingQueue> getClientQueues() {
|
||||
List<PacketSendingQueue> result = new ArrayList<PacketSendingQueue>();
|
||||
List<PacketSendingQueue> result = new ArrayList<>();
|
||||
|
||||
for (QueueContainer queue : playerSendingQueues.values())
|
||||
result.add(queue.getClientQueue());
|
||||
|
||||
@@ -17,13 +17,12 @@
|
||||
|
||||
package com.comphenix.protocol.async;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Queue;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,7 +8,6 @@ import java.util.PriorityQueue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Ticker;
|
||||
@@ -49,19 +48,14 @@ public class ExpireHashMap<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<K, ExpireEntry> keyLookup = new HashMap<K, ExpireEntry>();
|
||||
private PriorityQueue<ExpireEntry> expireQueue = new PriorityQueue<ExpireEntry>();
|
||||
private final Map<K, ExpireEntry> keyLookup = new HashMap<>();
|
||||
private final PriorityQueue<ExpireEntry> expireQueue = new PriorityQueue<>();
|
||||
|
||||
// View of keyLookup with direct values
|
||||
private Map<K, V> valueView = Maps.transformValues(keyLookup, new Function<ExpireEntry, V>() {
|
||||
@Override
|
||||
public V apply(ExpireEntry entry) {
|
||||
return entry.expireValue;
|
||||
}
|
||||
});
|
||||
private final Map<K, V> valueView = Maps.transformValues(keyLookup, entry -> entry.expireValue);
|
||||
|
||||
// Supplied by the constructor
|
||||
private Ticker ticker;
|
||||
private final Ticker ticker;
|
||||
|
||||
/**
|
||||
* Construct a new hash map where each entry may expire at a given time.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.comphenix.protocol.collections;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Represents a very quick integer-based lookup map, with a fixed key space size.
|
||||
@@ -16,15 +16,6 @@ public class IntegerMap<T> {
|
||||
private T[] array;
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* Construct a new integer map.
|
||||
* @param <T> Parameter type
|
||||
* @return A new integer map.
|
||||
*/
|
||||
public static <T> IntegerMap<T> newMap() {
|
||||
return new IntegerMap<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new integer map with a default capacity.
|
||||
*/
|
||||
@@ -128,7 +119,7 @@ public class IntegerMap<T> {
|
||||
* @return The Integer map.
|
||||
*/
|
||||
public Map<Integer, Object> toMap() {
|
||||
Map<Integer, Object> map = Maps.newHashMap();
|
||||
final Map<Integer, Object> map = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i] != null) {
|
||||
|
||||
+53
-49
@@ -2,21 +2,25 @@
|
||||
* 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 com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.injector.PrioritizedListener;
|
||||
import com.google.common.collect.Iterables;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -24,81 +28,78 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.injector.PrioritizedListener;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* A thread-safe implementation of a listener multimap.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class AbstractConcurrentListenerMultimap<TListener> {
|
||||
public abstract class AbstractConcurrentListenerMultimap<T> {
|
||||
|
||||
// The core of our map
|
||||
private ConcurrentMap<PacketType, SortedCopyOnWriteArray<PrioritizedListener<TListener>>> mapListeners;
|
||||
|
||||
private final ConcurrentMap<PacketType, SortedCopyOnWriteArray<PrioritizedListener<T>>> mapListeners;
|
||||
|
||||
public AbstractConcurrentListenerMultimap() {
|
||||
mapListeners = new ConcurrentHashMap<PacketType, SortedCopyOnWriteArray<PrioritizedListener<TListener>>>();
|
||||
this.mapListeners = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a listener to its requested list of packet receivers.
|
||||
* @param listener - listener with a list of packets to receive notifications for.
|
||||
*
|
||||
* @param listener - listener with a list of packets to receive notifications for.
|
||||
* @param whitelist - the packet whitelist to use.
|
||||
*/
|
||||
public void addListener(TListener listener, ListeningWhitelist whitelist) {
|
||||
PrioritizedListener<TListener> prioritized = new PrioritizedListener<TListener>(listener, whitelist.getPriority());
|
||||
|
||||
public void addListener(T listener, ListeningWhitelist whitelist) {
|
||||
PrioritizedListener<T> prioritized = new PrioritizedListener<>(listener, whitelist.getPriority());
|
||||
for (PacketType type : whitelist.getTypes()) {
|
||||
addListener(type, prioritized);
|
||||
this.addListener(type, prioritized);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add the listener to a specific packet notifcation list
|
||||
private void addListener(PacketType type, PrioritizedListener<TListener> listener) {
|
||||
SortedCopyOnWriteArray<PrioritizedListener<TListener>> list = mapListeners.get(type);
|
||||
|
||||
private void addListener(PacketType type, PrioritizedListener<T> listener) {
|
||||
SortedCopyOnWriteArray<PrioritizedListener<T>> list = this.mapListeners.get(type);
|
||||
|
||||
// We don't want to create this for every lookup
|
||||
if (list == null) {
|
||||
// It would be nice if we could use a PriorityBlockingQueue, but it doesn't preseve iterator order,
|
||||
// which is a essential feature for our purposes.
|
||||
final SortedCopyOnWriteArray<PrioritizedListener<TListener>> value = new SortedCopyOnWriteArray<PrioritizedListener<TListener>>();
|
||||
final SortedCopyOnWriteArray<PrioritizedListener<T>> value = new SortedCopyOnWriteArray<PrioritizedListener<T>>();
|
||||
|
||||
// We may end up creating multiple multisets, but we'll agree on which to use
|
||||
list = mapListeners.putIfAbsent(type, value);
|
||||
|
||||
list = this.mapListeners.putIfAbsent(type, value);
|
||||
|
||||
if (list == null) {
|
||||
list = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Thread safe
|
||||
list.add(listener);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the given listener from the packet event list.
|
||||
* @param listener - listener to remove.
|
||||
*
|
||||
* @param listener - listener to remove.
|
||||
* @param whitelist - the packet whitelist that was used.
|
||||
* @return Every packet ID that was removed due to no listeners.
|
||||
*/
|
||||
public List<PacketType> removeListener(TListener listener, ListeningWhitelist whitelist) {
|
||||
public List<PacketType> removeListener(T listener, ListeningWhitelist whitelist) {
|
||||
List<PacketType> removedPackets = new ArrayList<PacketType>();
|
||||
|
||||
|
||||
// Again, not terribly efficient. But adding or removing listeners should be a rare event.
|
||||
for (PacketType type : whitelist.getTypes()) {
|
||||
SortedCopyOnWriteArray<PrioritizedListener<TListener>> list = mapListeners.get(type);
|
||||
|
||||
SortedCopyOnWriteArray<PrioritizedListener<T>> list = this.mapListeners.get(type);
|
||||
|
||||
// Remove any listeners
|
||||
if (list != null) {
|
||||
// Don't remove from newly created lists
|
||||
if (list.size() > 0) {
|
||||
// Remove this listener. Note that priority is generally ignored.
|
||||
list.remove(new PrioritizedListener<TListener>(listener, whitelist.getPriority()));
|
||||
|
||||
list.remove(new PrioritizedListener<T>(listener, whitelist.getPriority()));
|
||||
|
||||
if (list.size() == 0) {
|
||||
mapListeners.remove(type);
|
||||
this.mapListeners.remove(type);
|
||||
removedPackets.add(type);
|
||||
}
|
||||
}
|
||||
@@ -107,38 +108,41 @@ public abstract class AbstractConcurrentListenerMultimap<TListener> {
|
||||
}
|
||||
return removedPackets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the registered listeners, in order from the lowest to the highest priority.
|
||||
* <p>
|
||||
* The returned list is thread-safe and doesn't require synchronization.
|
||||
*
|
||||
* @param type - packet type.
|
||||
* @return Registered listeners.
|
||||
*/
|
||||
public Collection<PrioritizedListener<TListener>> getListener(PacketType type) {
|
||||
return mapListeners.get(type);
|
||||
public Collection<PrioritizedListener<T>> getListener(PacketType type) {
|
||||
return this.mapListeners.get(type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve every listener.
|
||||
*
|
||||
* @return Every listener.
|
||||
*/
|
||||
public Iterable<PrioritizedListener<TListener>> values() {
|
||||
return Iterables.concat(mapListeners.values());
|
||||
public Iterable<PrioritizedListener<T>> values() {
|
||||
return Iterables.concat(this.mapListeners.values());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve every registered packet type:
|
||||
*
|
||||
* @return Registered packet type.
|
||||
*/
|
||||
public Set<PacketType> keySet() {
|
||||
return mapListeners.keySet();
|
||||
return this.mapListeners.keySet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove all packet listeners.
|
||||
*/
|
||||
protected void clearListeners() {
|
||||
mapListeners.clear();
|
||||
this.mapListeners.clear();
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,9 @@ import com.google.common.base.Preconditions;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.RemovalListener;
|
||||
import com.google.common.collect.AbstractIterator;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
@@ -73,7 +73,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
* @return Concurrent player map.
|
||||
*/
|
||||
public static <T> ConcurrentPlayerMap<T> usingAddress() {
|
||||
return new ConcurrentPlayerMap<T>(PlayerKey.ADDRESS);
|
||||
return new ConcurrentPlayerMap<>(PlayerKey.ADDRESS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +82,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
* @return Concurrent player map.
|
||||
*/
|
||||
public static <T> ConcurrentPlayerMap<T> usingName() {
|
||||
return new ConcurrentPlayerMap<T>(PlayerKey.NAME);
|
||||
return new ConcurrentPlayerMap<>(PlayerKey.NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +108,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
* @return The value map.
|
||||
*/
|
||||
private ConcurrentMap<Object, TValue> createValueMap() {
|
||||
return Maps.newConcurrentMap();
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,7 +162,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
* @return The player with the given key, or NULL if not found.
|
||||
*/
|
||||
private Player findOnlinePlayer(Object key) {
|
||||
for (Player player : Util.getOnlinePlayers()) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (key.equals(keyMethod.apply(player))) {
|
||||
return player;
|
||||
}
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.concurrency;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents a very quick integer set that uses a lookup table to store membership.
|
||||
* <p>
|
||||
* This class is intentionally missing a size method.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class IntegerSet {
|
||||
private final boolean[] array;
|
||||
|
||||
/**
|
||||
* Initialize a lookup table with the given maximum number of elements.
|
||||
* <p>
|
||||
* This creates a set for elements in the range [0, count).
|
||||
* <p>
|
||||
* Formally, the current set must be a subset of [0, 1, 2, ..., count - 1].
|
||||
* @param maximumCount - maximum element value and count.
|
||||
*/
|
||||
public IntegerSet(int maximumCount) {
|
||||
this.array = new boolean[maximumCount];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a lookup table with a given maximum and value list.
|
||||
* <p>
|
||||
* The provided elements must be in the range [0, count).
|
||||
* @param maximumCount - the maximum element value and count.
|
||||
* @param values - the elements to add to the set.
|
||||
*/
|
||||
public IntegerSet(int maximumCount, Collection<Integer> values) {
|
||||
this.array = new boolean[maximumCount];
|
||||
addAll(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not the given element exists in the set.
|
||||
* @param element - the element to check. Must be in the range [0, count).
|
||||
* @return TRUE if the given element exists, FALSE otherwise.
|
||||
*/
|
||||
public boolean contains(int element) {
|
||||
return array[element];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given element to the set, or do nothing if it already exists.
|
||||
* @param element - element to add.
|
||||
* @throws ArrayIndexOutOfBoundsException If the given element is not in the range [0, count).
|
||||
*/
|
||||
public void add(int element) {
|
||||
array[element] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given collection of elements to the set.
|
||||
* @param packets - elements to add.
|
||||
*/
|
||||
public void addAll(Collection<Integer> packets) {
|
||||
for (Integer id : packets) {
|
||||
add(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given element from the set, or do nothing if it's already removed.
|
||||
* @param element - element to remove.
|
||||
*/
|
||||
public void remove(int element) {
|
||||
// We don't actually care if the caller tries to remove an element outside the valid set
|
||||
if (element >= 0 && element < array.length)
|
||||
array[element] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove every element from the set.
|
||||
*/
|
||||
public void clear() {
|
||||
Arrays.fill(array, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the current IntegerSet to an equivalent HashSet.
|
||||
* @return The resulting HashSet.
|
||||
*/
|
||||
public Set<Integer> toSet() {
|
||||
Set<Integer> elements = new HashSet<Integer>();
|
||||
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i])
|
||||
elements.add(i);
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
@@ -1,125 +1,133 @@
|
||||
package com.comphenix.protocol.concurrency;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents a concurrent set of packet types.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PacketTypeSet {
|
||||
private Set<PacketType> types = Collections.newSetFromMap(Maps.<PacketType, Boolean>newConcurrentMap());
|
||||
private Set<Class<?>> classes = Collections.newSetFromMap(Maps.<Class<?>, Boolean>newConcurrentMap());
|
||||
|
||||
|
||||
private final Set<PacketType> types;
|
||||
private final Set<Class<?>> classes;
|
||||
|
||||
public PacketTypeSet() {
|
||||
// Do nothing
|
||||
this.types = new HashSet<>(16, 0.9f);
|
||||
this.classes = new HashSet<>(16, 0.9f);
|
||||
}
|
||||
|
||||
|
||||
public PacketTypeSet(Collection<? extends PacketType> values) {
|
||||
for (PacketType type : values) {
|
||||
addType(type);
|
||||
}
|
||||
this.types = new HashSet<>(values.size(), 0.9f);
|
||||
this.classes = new HashSet<>(values.size(), 0.9f);
|
||||
|
||||
this.addAll(values);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a particular type to the set.
|
||||
*
|
||||
* @param type - the type to add.
|
||||
*/
|
||||
public synchronized void addType(PacketType type) {
|
||||
public void addType(PacketType type) {
|
||||
this.types.add(type);
|
||||
|
||||
Class<?> packetClass = type.getPacketClass();
|
||||
types.add(Preconditions.checkNotNull(type, "type cannot be NULL."));
|
||||
|
||||
if (packetClass != null) {
|
||||
classes.add(type.getPacketClass());
|
||||
this.classes.add(packetClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add the given types to the set of packet types.
|
||||
*
|
||||
* @param types - the types to add.
|
||||
*/
|
||||
public synchronized void addAll(Iterable<? extends PacketType> types) {
|
||||
public void addAll(Iterable<? extends PacketType> types) {
|
||||
for (PacketType type : types) {
|
||||
addType(type);
|
||||
this.addType(type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove a particular type to the set.
|
||||
*
|
||||
* @param type - the type to remove.
|
||||
*/
|
||||
public synchronized void removeType(PacketType type) {
|
||||
public void removeType(PacketType type) {
|
||||
this.types.remove(type);
|
||||
|
||||
Class<?> packetClass = type.getPacketClass();
|
||||
types.remove(Preconditions.checkNotNull(type, "type cannot be NULL."));
|
||||
|
||||
if (packetClass != null) {
|
||||
classes.remove(packetClass);
|
||||
this.classes.remove(packetClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove the given types from the set.
|
||||
*
|
||||
* @param types Types to remove
|
||||
*/
|
||||
public synchronized void removeAll(Iterable<? extends PacketType> types) {
|
||||
public void removeAll(Iterable<? extends PacketType> types) {
|
||||
for (PacketType type : types) {
|
||||
removeType(type);
|
||||
this.removeType(type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given packet type exists in the set.
|
||||
*
|
||||
* @param type - the type to find.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public boolean contains(PacketType type) {
|
||||
return types.contains(type);
|
||||
return this.types.contains(type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if a packet type with the given packet class exists in the set.
|
||||
*
|
||||
* @param packetClass - the class to find.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public boolean contains(Class<?> packetClass) {
|
||||
return classes.contains(packetClass);
|
||||
return this.classes.contains(packetClass);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the type of a packet is in the current set.
|
||||
*
|
||||
* @param packet - the packet.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean containsPacket(Object packet) {
|
||||
if (packet == null)
|
||||
return false;
|
||||
return classes.contains(packet.getClass());
|
||||
return packet != null && this.classes.contains(packet.getClass());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a view of this packet type set.
|
||||
*
|
||||
* @return The packet type values.
|
||||
*/
|
||||
public Set<PacketType> values() {
|
||||
return types;
|
||||
return ImmutableSet.copyOf(this.types);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the number of entries in the set.
|
||||
*
|
||||
* @return The number of entries.
|
||||
*/
|
||||
public int size() {
|
||||
return types.size();
|
||||
return this.types.size();
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
types.clear();
|
||||
classes.clear();
|
||||
|
||||
public void clear() {
|
||||
this.types.clear();
|
||||
this.classes.clear();
|
||||
}
|
||||
}
|
||||
@@ -2,243 +2,236 @@
|
||||
* 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 com.google.common.base.Objects;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* An implicitly sorted array list that preserves insertion order and maintains duplicates.
|
||||
*
|
||||
* @param <T> - type of the elements in the list.
|
||||
*/
|
||||
public class SortedCopyOnWriteArray<T extends Comparable<T>> implements Collection<T> {
|
||||
|
||||
// Prevent reordering
|
||||
private volatile List<T> list;
|
||||
|
||||
|
||||
/**
|
||||
* Construct an empty sorted array.
|
||||
*/
|
||||
public SortedCopyOnWriteArray() {
|
||||
list = new ArrayList<T>();
|
||||
this.list = new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a sorted array from the given list. The elements will be automatically sorted.
|
||||
*
|
||||
* @param wrapped - the collection whose elements are to be placed into the list.
|
||||
*/
|
||||
public SortedCopyOnWriteArray(Collection<T> wrapped) {
|
||||
this.list = new ArrayList<T>(wrapped);
|
||||
this.list = new ArrayList<>(wrapped);
|
||||
Collections.sort(this.list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sorted array from the given list.
|
||||
* @param wrapped - the collection whose elements are to be placed into the list.
|
||||
* @param sort - TRUE to automatically sort the collection, FALSE if it is already sorted.
|
||||
*/
|
||||
public SortedCopyOnWriteArray(Collection<T> wrapped, boolean sort) {
|
||||
this.list = new ArrayList<T>(wrapped);
|
||||
|
||||
if (sort) {
|
||||
Collections.sort(list);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts the given element in the proper location.
|
||||
*
|
||||
* @param value - element to insert.
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean add(T value) {
|
||||
// We use NULL as a special marker, so we don't allow it
|
||||
if (value == null)
|
||||
throw new IllegalArgumentException("value cannot be NULL");
|
||||
|
||||
List<T> copy = new ArrayList<T>();
|
||||
// We use NULL as a special marker, so we don't allow it
|
||||
if (value == null) throw new IllegalArgumentException("value cannot be NULL");
|
||||
|
||||
List<T> copy = new ArrayList<>();
|
||||
boolean inserted = false;
|
||||
|
||||
for (T element : this.list) {
|
||||
// If the value is now greater than the current element, it should be placed right before it
|
||||
if (!inserted && value.compareTo(element) < 0) {
|
||||
copy.add(value);
|
||||
inserted = true;
|
||||
}
|
||||
copy.add(element);
|
||||
}
|
||||
|
||||
// Don't forget to add it
|
||||
if (!inserted) {
|
||||
copy.add(value);
|
||||
}
|
||||
|
||||
this.list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (T element : list) {
|
||||
// If the value is now greater than the current element, it should be placed right before it
|
||||
if (value != null && value.compareTo(element) < 0) {
|
||||
copy.add(value);
|
||||
value = null;
|
||||
}
|
||||
copy.add(element);
|
||||
}
|
||||
|
||||
// Don't forget to add it
|
||||
if (value != null)
|
||||
copy.add(value);
|
||||
|
||||
list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addAll(Collection<? extends T> values) {
|
||||
if (values == null)
|
||||
throw new IllegalArgumentException("values cannot be NULL");
|
||||
if (values.size() == 0)
|
||||
public synchronized boolean addAll(Collection<? extends T> values) {
|
||||
if (values == null) throw new IllegalArgumentException("values cannot be NULL");
|
||||
|
||||
if (values.size() == 0) {
|
||||
return false;
|
||||
|
||||
List<T> copy = new ArrayList<T>();
|
||||
|
||||
// Insert the new content and sort it
|
||||
copy.addAll(list);
|
||||
copy.addAll(values);
|
||||
Collections.sort(copy);
|
||||
|
||||
list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from the list by making a new list with every element except the one given.
|
||||
* <p>
|
||||
* Objects will be compared using the given objects equals() method.
|
||||
* @param value - value to remove.
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean remove(Object value) {
|
||||
List<T> copy = new ArrayList<T>();
|
||||
boolean result = false;
|
||||
|
||||
// Note that there's not much to be gained from using BinarySearch, as we
|
||||
// have to copy (and thus read) the entire list regardless.
|
||||
|
||||
// Copy every element except the one given to us.
|
||||
for (T element : list) {
|
||||
if (!Objects.equal(value, element)) {
|
||||
copy.add(element);
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<T> copy = new ArrayList<>(this.list);
|
||||
|
||||
// Insert the new content and sort it
|
||||
copy.addAll(values);
|
||||
Collections.sort(copy);
|
||||
|
||||
this.list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from the list by making a new list with every element except the one given.
|
||||
* <p>
|
||||
* Objects will be compared using the given objects equals() method.
|
||||
*
|
||||
* @param value - value to remove.
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean remove(Object value) {
|
||||
List<T> copy = new ArrayList<>();
|
||||
boolean result = false;
|
||||
|
||||
// Note that there's not much to be gained from using BinarySearch, as we
|
||||
// have to copy (and thus read) the entire list regardless.
|
||||
|
||||
// Copy every element except the one given to us.
|
||||
for (T element : this.list) {
|
||||
if (!Objects.equal(value, element)) {
|
||||
copy.add(element);
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.list = copy;
|
||||
return result;
|
||||
}
|
||||
|
||||
list = copy;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> values) {
|
||||
// Special cases
|
||||
if (values == null)
|
||||
throw new IllegalArgumentException("values cannot be NULL");
|
||||
if (values.size() == 0)
|
||||
if (values == null) throw new IllegalArgumentException("values cannot be NULL");
|
||||
|
||||
if (values.size() == 0) {
|
||||
return false;
|
||||
|
||||
List<T> copy = new ArrayList<T>();
|
||||
|
||||
copy.addAll(list);
|
||||
}
|
||||
|
||||
List<T> copy = new ArrayList<>(this.list);
|
||||
copy.removeAll(values);
|
||||
|
||||
list = copy;
|
||||
|
||||
this.list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> values) {
|
||||
// Special cases
|
||||
if (values == null)
|
||||
throw new IllegalArgumentException("values cannot be NULL");
|
||||
if (values.size() == 0)
|
||||
return false;
|
||||
|
||||
List<T> copy = new ArrayList<T>();
|
||||
|
||||
copy.addAll(list);
|
||||
if (values == null) throw new IllegalArgumentException("values cannot be NULL");
|
||||
|
||||
if (values.isEmpty()) return false;
|
||||
|
||||
List<T> copy = new ArrayList<>(this.list);
|
||||
copy.removeAll(values);
|
||||
|
||||
list = copy;
|
||||
|
||||
this.list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from the list by making a copy of every element except the one with the given index.
|
||||
* @param index - index of the element to remove.
|
||||
*/
|
||||
public synchronized void remove(int index) {
|
||||
List<T> copy = new ArrayList<T>(list);
|
||||
|
||||
copy.remove(index);
|
||||
list = copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an element by index.
|
||||
* @param index - index of element to retrieve.
|
||||
* @return The element at the given location.
|
||||
*/
|
||||
public T get(int index) {
|
||||
return list.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the size of the list.
|
||||
* @return Size of the list.
|
||||
*/
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an iterator over the elements in the given list.
|
||||
* Warning: No not attempt to remove elements using the iterator.
|
||||
*/
|
||||
public Iterator<T> iterator() {
|
||||
return Iterables.unmodifiableIterable(list).iterator();
|
||||
/**
|
||||
* Removes from the list by making a copy of every element except the one with the given index.
|
||||
*
|
||||
* @param index - index of the element to remove.
|
||||
*/
|
||||
public synchronized void remove(int index) {
|
||||
List<T> copy = new ArrayList<>(this.list);
|
||||
|
||||
copy.remove(index);
|
||||
this.list = copy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves an element by index.
|
||||
*
|
||||
* @param index - index of element to retrieve.
|
||||
* @return The element at the given location.
|
||||
*/
|
||||
public T get(int index) {
|
||||
return this.list.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the size of the list.
|
||||
*
|
||||
* @return Size of the list.
|
||||
*/
|
||||
public int size() {
|
||||
return this.list.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an iterator over the elements in the given list. Warning: No not attempt to remove elements using the
|
||||
* iterator.
|
||||
*/
|
||||
public Iterator<T> iterator() {
|
||||
return Iterables.unmodifiableIterable(this.list).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
list = new ArrayList<T>();
|
||||
this.list = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object value) {
|
||||
return list.contains(value);
|
||||
return this.list.contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> values) {
|
||||
return list.containsAll(values);
|
||||
return this.list.containsAll(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return list.isEmpty();
|
||||
return this.list.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return list.toArray();
|
||||
return this.list.toArray();
|
||||
}
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return list.toArray(a);
|
||||
return this.list.toArray(a);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return list.toString();
|
||||
return this.list.toString();
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,11 @@ package com.comphenix.protocol.error;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.error.Report.ReportBuilder;
|
||||
import com.comphenix.protocol.reflect.PrettyPrinter;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents a basic error reporter that prints error reports to the standard error stream.
|
||||
* <p>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.comphenix.protocol.error;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.error.Report.ReportBuilder;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Construct an error reporter that delegates to another error reporter.
|
||||
* @author Kristian
|
||||
|
||||
@@ -30,11 +30,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang.builder.ToStringStyle;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.collections.ExpireHashMap;
|
||||
import com.comphenix.protocol.error.Report.ReportBuilder;
|
||||
@@ -43,6 +39,11 @@ import com.comphenix.protocol.reflect.PrettyPrinter;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.primitives.Primitives;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang.builder.ToStringStyle;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Internal class used to handle exceptions.
|
||||
*
|
||||
@@ -89,7 +90,7 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
|
||||
// Reports to ignore
|
||||
private ExpireHashMap<Report, Boolean> rateLimited = new ExpireHashMap<Report, Boolean>();
|
||||
private Object rateLock = new Object();
|
||||
private final Object rateLock = new Object();
|
||||
|
||||
/**
|
||||
* Create a default error reporting system.
|
||||
@@ -202,14 +203,14 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
|
||||
// See if we should print the full error
|
||||
if (errorCount < getMaxErrorCount()) {
|
||||
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception occured in " +
|
||||
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception occurred in " +
|
||||
methodName + " for " + pluginName, error);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// Nope - only print the error count occationally
|
||||
// Nope - only print the error count occasionally
|
||||
if (isPowerOfTwo(errorCount)) {
|
||||
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception number " + errorCount + " occured in " +
|
||||
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception number " + errorCount + " occurred in " +
|
||||
methodName + " for " + pluginName, error);
|
||||
}
|
||||
return false;
|
||||
@@ -390,9 +391,9 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
writer.println(addPrefix(Bukkit.getServer().getVersion(), SECOND_LEVEL_PREFIX));
|
||||
|
||||
// Inform of this occurrence
|
||||
if (ERROR_PERMISSION != null) {
|
||||
if (ERROR_PERMISSION != null && ProtocolLibrary.getConfig().isChatWarnings()) {
|
||||
Bukkit.getServer().broadcast(
|
||||
String.format("Error %s (%s) occured in %s.", report.getReportMessage(), report.getException(), sender),
|
||||
String.format("Error %s (%s) occurred in %s.", report.getReportMessage(), report.getException(), sender),
|
||||
ERROR_PERMISSION
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
package com.comphenix.protocol.error;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.error.Report.ReportBuilder;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents an object that can forward an error {@link Report} to the display and permanent storage.
|
||||
*
|
||||
|
||||
@@ -4,11 +4,11 @@ import java.io.File;
|
||||
import java.net.URLDecoder;
|
||||
import java.security.CodeSource;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
public final class PluginContext {
|
||||
// Determine plugin folder
|
||||
private static File pluginFolder;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.comphenix.protocol.error;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a error or warning report.
|
||||
*
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.comphenix.protocol.error;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.error.Report.ReportBuilder;
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents an error reporter that rethrows every exception instead.
|
||||
* @author Kristian
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.Array;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
@@ -7,10 +12,12 @@ import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.StreamSerializer;
|
||||
import com.comphenix.protocol.wrappers.*;
|
||||
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtBase;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
@@ -23,10 +30,6 @@ import org.bukkit.inventory.MerchantRecipe;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AbstractStructure {
|
||||
protected transient Object handle;
|
||||
protected transient StructureModifier<Object> structureModifier;
|
||||
@@ -294,17 +297,6 @@ public abstract class AbstractStructure {
|
||||
BukkitConverters.getEntityTypeConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for chunk positions.
|
||||
* @return A modifier for a ChunkPosition.
|
||||
*/
|
||||
public StructureModifier<ChunkPosition> getPositionModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getChunkPositionClass(),
|
||||
ChunkPosition.getConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for block positions.
|
||||
* @return A modifier for a BlockPosition.
|
||||
@@ -376,21 +368,6 @@ public abstract class AbstractStructure {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for collections of chunk positions.
|
||||
* <p>
|
||||
* This modifier will automatically marshal between the visible ProtocolLib ChunkPosition and the
|
||||
* internal Minecraft ChunkPosition.
|
||||
*
|
||||
* @return A modifier for ChunkPosition list fields.
|
||||
*/
|
||||
public StructureModifier<List<ChunkPosition>> getPositionCollectionModifier() {
|
||||
// Convert to and from the ProtocolLib wrapper
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(ChunkPosition.getConverter()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for collections of chunk positions.
|
||||
* <p>
|
||||
@@ -420,6 +397,17 @@ public abstract class AbstractStructure {
|
||||
BukkitConverters.getListConverter(BukkitConverters.getWatchableObjectConverter()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for collections of data values.
|
||||
* @return A modifier for data values.
|
||||
*/
|
||||
public StructureModifier<List<WrappedDataValue>> getDataValueCollectionModifier() {
|
||||
// Convert to and from the ProtocolLib wrapper
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(BukkitConverters.getDataValueConverter()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for block fields.
|
||||
* <p>
|
||||
@@ -648,6 +636,20 @@ public abstract class AbstractStructure {
|
||||
EnumWrappers.getPlayerInfoActionConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for an EnumSet of PlayerInfos.
|
||||
* @return A modifier for an EnumSet of PlayerInfo fields.
|
||||
*/
|
||||
public StructureModifier<Set<EnumWrappers.PlayerInfoAction>> getPlayerInfoActions() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.withType(
|
||||
EnumSet.class,
|
||||
Converters.collection(
|
||||
EnumWrappers.getPlayerInfoActionConverter(),
|
||||
generic -> EnumSet.noneOf(EnumWrappers.PlayerInfoAction.class),
|
||||
specific -> EnumWrappers.createEmptyEnumSet(EnumWrappers.getPlayerInfoActionClass())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the TitleAction enum in 1.8.
|
||||
* @return A modifier for TitleAction enum fields.
|
||||
@@ -924,6 +926,86 @@ public abstract class AbstractStructure {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for Instants in (mostly for use in 1.19+)
|
||||
* @return The Structure Modifier
|
||||
*/
|
||||
public StructureModifier<Instant> getInstants() {
|
||||
return structureModifier.withType(Instant.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for profile public keys in 1.9
|
||||
* @return The Structure Modifier
|
||||
*/
|
||||
public StructureModifier<WrappedProfilePublicKey> getProfilePublicKeys() {
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getProfilePublicKeyClass(),
|
||||
BukkitConverters.getWrappedProfilePublicKeyConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for profile public key data in 1.9
|
||||
* @return The Structure Modifier
|
||||
*/
|
||||
public StructureModifier<WrappedProfileKeyData> getProfilePublicKeyData() {
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getProfilePublicKeyDataClass(),
|
||||
BukkitConverters.getWrappedPublicKeyDataConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for LevelChunkPacketData in 1.18+
|
||||
*
|
||||
* @return The Structure Modifier
|
||||
*/
|
||||
public StructureModifier<WrappedLevelChunkData.ChunkData> getLevelChunkData() {
|
||||
return structureModifier.withType(MinecraftReflection.getLevelChunkPacketDataClass(), BukkitConverters.getWrappedChunkDataConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for LightUpdatePacketData in 1.18+
|
||||
*
|
||||
* @return The Structure Modifier
|
||||
*/
|
||||
public StructureModifier<WrappedLevelChunkData.LightData> getLightUpdateData() {
|
||||
return structureModifier.withType(MinecraftReflection.getLightUpdatePacketDataClass(), BukkitConverters.getWrappedLightDataConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return read/write structure for login encryption packets
|
||||
*/
|
||||
public StructureModifier<Either<byte[], WrappedSaltedSignature>> getLoginSignatures() {
|
||||
return getEithers(Converters.passthrough(byte[].class), BukkitConverters.getWrappedSignatureConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return read/writer structure direct access to signature data like chat messages
|
||||
*/
|
||||
public StructureModifier<WrappedSaltedSignature> getSignatures() {
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getSaltedSignatureClass(),
|
||||
BukkitConverters.getWrappedSignatureConverter()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param leftConverter converter for left values
|
||||
* @param rightConverter converter for right values
|
||||
* @return ProtocolLib's read/write structure for Mojang either structures
|
||||
* @param <L> left data type after converting from NMS
|
||||
* @param <R> right data type after converting from NMS
|
||||
*/
|
||||
public <L, R> StructureModifier<Either<L, R>> getEithers(EquivalentConverter<L> leftConverter,
|
||||
EquivalentConverter<R> rightConverter) {
|
||||
return structureModifier.withType(
|
||||
com.mojang.datafixers.util.Either.class,
|
||||
BukkitConverters.getEitherConverter(
|
||||
leftConverter, rightConverter
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the Map class.
|
||||
* @param keyConverter Converter for map keys
|
||||
@@ -1000,6 +1082,19 @@ public abstract class AbstractStructure {
|
||||
structureModifier.getField(index).getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for an optional, passing
|
||||
* the value of the optional field through the given converter
|
||||
* if present.
|
||||
*
|
||||
* @param converter Converter for internal element of optional, if present
|
||||
* @param <T> The inner type of the optional
|
||||
* @return The modifier
|
||||
*/
|
||||
public <T> StructureModifier<Optional<T>> getOptionals(EquivalentConverter<T> converter) {
|
||||
return structureModifier.withType(Optional.class, Converters.optional(converter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an equivalent converter for ItemStack arrays.
|
||||
* @author Kristian
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
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) {
|
||||
@@ -30,6 +29,10 @@ public class InternalStructure extends AbstractStructure {
|
||||
}
|
||||
};
|
||||
|
||||
public static EquivalentConverter<InternalStructure> getConverter() {
|
||||
return CONVERTER;
|
||||
}
|
||||
|
||||
public StructureModifier<InternalStructure> getStructures() {
|
||||
return structureModifier.withType(Object.class, CONVERTER);
|
||||
}
|
||||
|
||||
@@ -2,31 +2,32 @@ package com.comphenix.protocol.events;
|
||||
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
|
||||
|
||||
/**
|
||||
* Represents additional options a listener may require.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public enum ListenerOptions {
|
||||
|
||||
/**
|
||||
* Retrieve the serialized client packet as it appears on the network stream.
|
||||
*/
|
||||
INTERCEPT_INPUT_BUFFER,
|
||||
|
||||
/**
|
||||
* Disable the automatic game phase detection that will normally force {@link GamePhase#LOGIN} when
|
||||
* a packet ID is known to be transmitted during login.
|
||||
* Disable the automatic game phase detection that will normally force {@link GamePhase#LOGIN} when a packet ID is
|
||||
* known to be transmitted during login.
|
||||
*/
|
||||
DISABLE_GAMEPHASE_DETECTION,
|
||||
|
||||
|
||||
/**
|
||||
* Do not verify that the owning plugin has a vaid plugin.yml.
|
||||
*/
|
||||
SKIP_PLUGIN_VERIFIER,
|
||||
|
||||
|
||||
/**
|
||||
* Notify ProtocolLib that {@link PacketListener#onPacketSending(PacketEvent)} is thread safe.
|
||||
*/
|
||||
ASYNC;
|
||||
ASYNC,
|
||||
|
||||
/**
|
||||
* Notify ProtocolLib that {@link PacketListener#onPacketReceiving(PacketEvent)} must be executed on the main server
|
||||
* thread.
|
||||
*/
|
||||
SYNC
|
||||
}
|
||||
@@ -21,16 +21,16 @@ import java.util.*;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Determines which packets will be observed by a listener, and with what priority.
|
||||
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ListeningWhitelist {
|
||||
|
||||
/**
|
||||
* A whitelist with no packets - indicates that the listener shouldn't observe any packets.
|
||||
*/
|
||||
@@ -55,8 +55,72 @@ public class ListeningWhitelist {
|
||||
this.options = EnumSet.noneOf(ListenerOptions.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given whitelist is empty or not.
|
||||
*
|
||||
* @param whitelist - the whitelist to test.
|
||||
* @return TRUE if the whitelist is empty, FALSE otherwise.
|
||||
*/
|
||||
public static boolean isEmpty(ListeningWhitelist whitelist) {
|
||||
if (whitelist == EMPTY_WHITELIST) {
|
||||
return true;
|
||||
} else if (whitelist == null) {
|
||||
return true;
|
||||
} else {
|
||||
return whitelist.getTypes().isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new builder of whitelists.
|
||||
*
|
||||
* @return New whitelist builder.
|
||||
*/
|
||||
public static Builder newBuilder() {
|
||||
return new Builder(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new builder of whitelists initialized to the same values as the template.
|
||||
*
|
||||
* @param template - the template object.
|
||||
* @return New whitelist builder.
|
||||
*/
|
||||
public static Builder newBuilder(ListeningWhitelist template) {
|
||||
return new Builder(template);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a copy of a given enum.
|
||||
*
|
||||
* @param options - the options to copy, or NULL to indicate the empty set.
|
||||
* @return A copy of the enum set.
|
||||
*/
|
||||
private static <T extends Enum<T>> EnumSet<T> safeEnumSet(Collection<T> options, Class<T> enumClass) {
|
||||
if (options != null && !options.isEmpty()) {
|
||||
return EnumSet.copyOf(options);
|
||||
} else {
|
||||
return EnumSet.noneOf(enumClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a copy of a given set.
|
||||
*
|
||||
* @param set - the set to copy.
|
||||
* @return The copied set.
|
||||
*/
|
||||
private static <T> Set<T> safeSet(Collection<T> set) {
|
||||
if (set != null) {
|
||||
return new HashSet<>(set);
|
||||
} else {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this whitelist has any enabled packets.
|
||||
*
|
||||
* @return TRUE if there are any packets, FALSE otherwise.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
@@ -65,6 +129,7 @@ public class ListeningWhitelist {
|
||||
|
||||
/**
|
||||
* Retrieve the priority in the execution order of the packet listener. Highest priority will be executed last.
|
||||
*
|
||||
* @return Execution order in terms of priority.
|
||||
*/
|
||||
public ListenerPriority getPriority() {
|
||||
@@ -73,22 +138,25 @@ public class ListeningWhitelist {
|
||||
|
||||
/**
|
||||
* Retrieves a set of the packets that will be observed by the listeners.
|
||||
*
|
||||
* @return Packet whitelist.
|
||||
*/
|
||||
public Set<PacketType> getTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve which game phase this listener is active under.
|
||||
*
|
||||
* @return The active game phase.
|
||||
*/
|
||||
public GamePhase getGamePhase() {
|
||||
return gamePhase;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve every special option associated with this whitelist.
|
||||
*
|
||||
* @return Every special option.
|
||||
*/
|
||||
public Set<ListenerOptions> getOptions() {
|
||||
@@ -100,20 +168,6 @@ public class ListeningWhitelist {
|
||||
return Objects.hashCode(priority, types, gamePhase, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given whitelist is empty or not.
|
||||
* @param whitelist - the whitelist to test.
|
||||
* @return TRUE if the whitelist is empty, FALSE otherwise.
|
||||
*/
|
||||
public static boolean isEmpty(ListeningWhitelist whitelist) {
|
||||
if (whitelist == EMPTY_WHITELIST)
|
||||
return true;
|
||||
else if (whitelist == null)
|
||||
return true;
|
||||
else
|
||||
return whitelist.getTypes().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj instanceof ListeningWhitelist) {
|
||||
@@ -129,67 +183,30 @@ public class ListeningWhitelist {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this == EMPTY_WHITELIST)
|
||||
if (this == EMPTY_WHITELIST) {
|
||||
return "EMPTY_WHITELIST";
|
||||
else
|
||||
return "ListeningWhitelist[priority=" + priority + ", packets=" + types + ", gamephase=" + gamePhase + ", options=" + options + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new builder of whitelists.
|
||||
* @return New whitelist builder.
|
||||
*/
|
||||
public static Builder newBuilder() {
|
||||
return new Builder(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new builder of whitelists initialized to the same values as the template.
|
||||
* @param template - the template object.
|
||||
* @return New whitelist builder.
|
||||
*/
|
||||
public static Builder newBuilder(ListeningWhitelist template) {
|
||||
return new Builder(template);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a copy of a given enum.
|
||||
* @param options - the options to copy, or NULL to indicate the empty set.
|
||||
* @return A copy of the enum set.
|
||||
*/
|
||||
private static <T extends Enum<T>> EnumSet<T> safeEnumSet(Collection<T> options, Class<T> enumClass) {
|
||||
if (options != null && !options.isEmpty()) {
|
||||
return EnumSet.copyOf(options);
|
||||
} else {
|
||||
return EnumSet.noneOf(enumClass);
|
||||
return "ListeningWhitelist[priority=" + priority + ", packets=" + types + ", gamephase=" + gamePhase
|
||||
+ ", options=" + options + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a copy of a given set.
|
||||
* @param set - the set to copy.
|
||||
* @return The copied set.
|
||||
*/
|
||||
private static <T> Set<T> safeSet(Collection<T> set) {
|
||||
if (set != null)
|
||||
return Sets.newHashSet(set);
|
||||
else
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a builder of whitelists.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
// Default values
|
||||
private ListenerPriority priority = ListenerPriority.NORMAL;
|
||||
private Set<PacketType> types = Sets.newHashSet();
|
||||
private Set<PacketType> types = new HashSet<>();
|
||||
private GamePhase gamePhase = GamePhase.PLAYING;
|
||||
private Set<ListenerOptions> options = Sets.newHashSet();
|
||||
|
||||
private Set<ListenerOptions> options = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Construct a new listening whitelist template.
|
||||
*
|
||||
* @param template - the template.
|
||||
*/
|
||||
private Builder(ListeningWhitelist template) {
|
||||
@@ -200,9 +217,10 @@ public class ListeningWhitelist {
|
||||
options(template.getOptions());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the priority to use when constructing new whitelists.
|
||||
*
|
||||
* @param priority - the priority.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -210,49 +228,55 @@ public class ListeningWhitelist {
|
||||
this.priority = priority;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the priority of the whitelist to monitor.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder monitor() {
|
||||
return priority(ListenerPriority.MONITOR);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the priority of the whitelist to normal.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder normal() {
|
||||
return priority(ListenerPriority.NORMAL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the priority of the whitelist to lowest.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder lowest() {
|
||||
return priority(ListenerPriority.LOWEST);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the priority of the whitelist to low.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder low() {
|
||||
return priority(ListenerPriority.LOW);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the priority of the whitelist to highest.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder highest() {
|
||||
return priority(ListenerPriority.HIGHEST);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the priority of the whitelist to high.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder high() {
|
||||
@@ -261,6 +285,7 @@ public class ListeningWhitelist {
|
||||
|
||||
/**
|
||||
* Set the whitelist of packet types to copy when constructing new whitelists.
|
||||
*
|
||||
* @param types - the whitelist of packets.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -268,9 +293,10 @@ public class ListeningWhitelist {
|
||||
this.types = safeSet(Sets.newHashSet(types));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the whitelist of packet types to copy when constructing new whitelists.
|
||||
*
|
||||
* @param types - the whitelist of packets.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -278,9 +304,10 @@ public class ListeningWhitelist {
|
||||
this.types = safeSet(types);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the gamephase to use when constructing new whitelists.
|
||||
*
|
||||
* @param gamePhase - the gamephase.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -288,16 +315,19 @@ public class ListeningWhitelist {
|
||||
this.gamePhase = gamePhase;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the gamephase to {@link GamePhase#BOTH}.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder gamePhaseBoth() {
|
||||
return gamePhase(GamePhase.BOTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the options to copy when constructing new whitelists.
|
||||
*
|
||||
* @param options - the options.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -305,9 +335,10 @@ public class ListeningWhitelist {
|
||||
this.options = safeSet(options);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the options to copy when constructing new whitelists.
|
||||
*
|
||||
* @param options - the options.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -315,9 +346,10 @@ public class ListeningWhitelist {
|
||||
this.options = safeSet(options);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the options to copy when constructing new whitelists.
|
||||
*
|
||||
* @param serverOptions - the options array.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -325,32 +357,36 @@ public class ListeningWhitelist {
|
||||
this.options = safeSet(Sets.newHashSet(serverOptions));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Options to merge into the current set of options.
|
||||
*
|
||||
* @param serverOptions - the options array.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder mergeOptions(ListenerOptions... serverOptions) {
|
||||
return mergeOptions(Arrays.asList(serverOptions));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Options to merge into the current set of options.
|
||||
*
|
||||
* @param serverOptions - the options array.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder mergeOptions(Collection<ListenerOptions> serverOptions) {
|
||||
if (options == null)
|
||||
if (options == null) {
|
||||
return options(serverOptions);
|
||||
|
||||
}
|
||||
|
||||
// Merge the options
|
||||
this.options.addAll(serverOptions);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new whitelist from the values in this builder.
|
||||
*
|
||||
* @return The new whitelist.
|
||||
*/
|
||||
public ListeningWhitelist build() {
|
||||
|
||||
@@ -2,82 +2,76 @@
|
||||
* 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.events;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents a listener that is notified of every sent and received packet.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class MonitorAdapter implements PacketListener {
|
||||
|
||||
private Plugin plugin;
|
||||
private ListeningWhitelist sending = ListeningWhitelist.EMPTY_WHITELIST;
|
||||
private ListeningWhitelist receiving = ListeningWhitelist.EMPTY_WHITELIST;
|
||||
private final Plugin plugin;
|
||||
private final ListeningWhitelist sending;
|
||||
private final ListeningWhitelist receiving;
|
||||
|
||||
public MonitorAdapter(Plugin plugin, ConnectionSide side) {
|
||||
initialize(plugin, side, getLogger(plugin));
|
||||
}
|
||||
|
||||
public MonitorAdapter(Plugin plugin, ConnectionSide side, Logger logger) {
|
||||
initialize(plugin, side, logger);
|
||||
}
|
||||
|
||||
private void initialize(Plugin plugin, ConnectionSide side, Logger logger) {
|
||||
this.plugin = plugin;
|
||||
|
||||
// Recover in case something goes wrong
|
||||
if (side.isForServer())
|
||||
this.sending = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getServerPacketTypes()).gamePhaseBoth().build();
|
||||
if (side.isForClient())
|
||||
this.receiving = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getClientPacketTypes()).gamePhaseBoth().build();
|
||||
// check the connection side and register the packets for the given side
|
||||
this.sending = side.isForServer() ? buildWhitelist(PacketRegistry.getServerPacketTypes()) : ListeningWhitelist.EMPTY_WHITELIST;
|
||||
this.receiving = side.isForClient() ? buildWhitelist(PacketRegistry.getClientPacketTypes()) : ListeningWhitelist.EMPTY_WHITELIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a logger, even if we're running in a CraftBukkit version that doesn't support it.
|
||||
* @param plugin - the plugin to retrieve.
|
||||
* @return The logger.
|
||||
*/
|
||||
private Logger getLogger(Plugin plugin) {
|
||||
try {
|
||||
return plugin.getLogger();
|
||||
} catch (NoSuchMethodError e) {
|
||||
return Logger.getLogger("Minecraft");
|
||||
}
|
||||
@Deprecated
|
||||
public MonitorAdapter(Plugin plugin, ConnectionSide side, Logger logger) {
|
||||
this(plugin, side);
|
||||
}
|
||||
|
||||
|
||||
private static ListeningWhitelist buildWhitelist(Collection<PacketType> packetTypes) {
|
||||
return ListeningWhitelist.newBuilder().monitor().gamePhaseBoth().types(packetTypes).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getSendingWhitelist() {
|
||||
return sending;
|
||||
return this.sending;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getReceivingWhitelist() {
|
||||
return receiving;
|
||||
return this.receiving;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,421 +1,153 @@
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import java.util.*;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.utility.ByteBufferInputStream;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.StreamSerializer;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Marker containing the serialized packet data seen from the network,
|
||||
* or output handlers that will serialize the current packet.
|
||||
*
|
||||
* Marker containing the serialized packet data seen from the network, or output handlers that will serialize the
|
||||
* current packet.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class NetworkMarker {
|
||||
public static class EmptyBufferMarker extends NetworkMarker {
|
||||
public EmptyBufferMarker(@Nonnull ConnectionSide side) {
|
||||
super(side, (byte[]) null, null);
|
||||
}
|
||||
public class NetworkMarker {
|
||||
|
||||
@Override
|
||||
protected DataInputStream skipHeader(DataInputStream input) throws IOException {
|
||||
throw new IllegalStateException("Buffer is empty.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteBuffer addHeader(ByteBuffer buffer, PacketType type) {
|
||||
throw new IllegalStateException("Buffer is empty.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream addHeader(DataInputStream input, PacketType type) {
|
||||
throw new IllegalStateException("Buffer is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
// Custom network handler
|
||||
private PriorityQueue<PacketOutputHandler> outputHandlers;
|
||||
// Post listeners
|
||||
private List<PacketPostListener> postListeners;
|
||||
// Post packets
|
||||
private List<ScheduledPacket> scheduledPackets;
|
||||
|
||||
// The input buffer
|
||||
private ByteBuffer inputBuffer;
|
||||
private final ConnectionSide side;
|
||||
// The input data
|
||||
private final PacketType type;
|
||||
|
||||
// Cache serializer too
|
||||
private StreamSerializer serializer;
|
||||
|
||||
/**
|
||||
* Construct a new network marker.
|
||||
* @param side - which side this marker belongs to.
|
||||
* @param inputBuffer - the read serialized packet data.
|
||||
* @param type - packet type
|
||||
*/
|
||||
public NetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer, PacketType type) {
|
||||
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
|
||||
this.inputBuffer = inputBuffer;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
private final ConnectionSide side;
|
||||
|
||||
// Post-processing of the packet
|
||||
private Set<PacketPostListener> postListeners;
|
||||
private Deque<ScheduledPacket> scheduledPackets;
|
||||
|
||||
/**
|
||||
* Construct a new network marker.
|
||||
* <p>
|
||||
* The input buffer is only non-null for client-side packets.
|
||||
*
|
||||
* @param side - which side this marker belongs to.
|
||||
* @param inputBuffer - the read serialized packet data.
|
||||
* @param type - packet type
|
||||
*/
|
||||
public NetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer, PacketType type) {
|
||||
public NetworkMarker(@Nonnull ConnectionSide side, PacketType type) {
|
||||
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
|
||||
this.type = type;
|
||||
|
||||
if (inputBuffer != null) {
|
||||
this.inputBuffer = ByteBuffer.wrap(inputBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether or not this marker belongs to a client or a server side packet.
|
||||
* @return The side the parent packet belongs to.
|
||||
*/
|
||||
public ConnectionSide getSide() {
|
||||
return side;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a utility class for serializing and deserializing Minecraft objects.
|
||||
* @return Serialization utility class.
|
||||
*/
|
||||
public StreamSerializer getSerializer() {
|
||||
if (serializer == null)
|
||||
serializer = new StreamSerializer();
|
||||
return serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the serialized packet data (excluding the header by default) from the network input stream.
|
||||
* <p>
|
||||
* The returned buffer is read-only. If the parent event is a server side packet this
|
||||
* method throws {@link IllegalStateException}.
|
||||
* <p>
|
||||
* It returns NULL if the packet was transmitted by a plugin locally.
|
||||
* @return A byte buffer containing the raw packet data read from the network.
|
||||
*/
|
||||
public ByteBuffer getInputBuffer() {
|
||||
return getInputBuffer(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the serialized packet data from the network input stream.
|
||||
* <p>
|
||||
* The returned buffer is read-only. If the parent event is a server side packet this
|
||||
* method throws {@link IllegalStateException}.
|
||||
* <p>
|
||||
* It returns NULL if the packet was transmitted by a plugin locally.
|
||||
* @param excludeHeader - whether or not to exclude the packet ID header.
|
||||
* @return A byte buffer containing the raw packet data read from the network.
|
||||
*/
|
||||
public ByteBuffer getInputBuffer(boolean excludeHeader) {
|
||||
if (side.isForServer())
|
||||
throw new IllegalStateException("Server-side packets have no input buffer.");
|
||||
|
||||
if (inputBuffer != null) {
|
||||
ByteBuffer result = inputBuffer.asReadOnlyBuffer();
|
||||
|
||||
try {
|
||||
if (excludeHeader)
|
||||
result = skipHeader(result);
|
||||
else
|
||||
result = addHeader(result, type);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot skip packet header.", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the serialized packet data (excluding the header by default) as an input stream.
|
||||
* <p>
|
||||
* The data is exactly the same as in {@link #getInputBuffer()}.
|
||||
* @see #getInputBuffer()
|
||||
* @return The incoming serialized packet data as a stream, or NULL if the packet was transmitted locally.
|
||||
*/
|
||||
public DataInputStream getInputStream() {
|
||||
return getInputStream(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the serialized packet data as an input stream.
|
||||
* <p>
|
||||
* The data is exactly the same as in {@link #getInputBuffer()}.
|
||||
* @see #getInputBuffer()
|
||||
* @param excludeHeader - whether or not to exclude the packet ID header.
|
||||
* @return The incoming serialized packet data as a stream, or NULL if the packet was transmitted locally.
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public DataInputStream getInputStream(boolean excludeHeader) {
|
||||
if (side.isForServer())
|
||||
throw new IllegalStateException("Server-side packets have no input buffer.");
|
||||
if (inputBuffer == null)
|
||||
return null;
|
||||
|
||||
DataInputStream input = new DataInputStream(
|
||||
new ByteArrayInputStream(inputBuffer.array())
|
||||
);
|
||||
|
||||
try {
|
||||
if (excludeHeader)
|
||||
input = skipHeader(input);
|
||||
else
|
||||
input = addHeader(input, type);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot skip packet header.", e);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the output handlers have to write a packet header.
|
||||
* @return TRUE if they do, FALSE otherwise.
|
||||
*/
|
||||
public boolean requireOutputHeader() {
|
||||
return MinecraftReflection.isUsingNetty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the given output handler for managing how the current packet will be written to the network stream.
|
||||
* <p>
|
||||
* Note that output handlers are not serialized, as most consumers will probably implement them using anonymous classes.
|
||||
* It is not safe to serialize anonymous classes, as their name depend on the order in which they are declared in the parent class.
|
||||
* <p>
|
||||
* This can only be invoked on server side packet events.
|
||||
* @param handler - the handler that will take part in serializing the packet.
|
||||
* @return TRUE if it was added, FALSE if it has already been added.
|
||||
*/
|
||||
public boolean addOutputHandler(@Nonnull PacketOutputHandler handler) {
|
||||
checkServerSide();
|
||||
Preconditions.checkNotNull(handler, "handler cannot be NULL.");
|
||||
|
||||
// Lazy initialization - it's imperative that we save space and time here
|
||||
if (outputHandlers == null) {
|
||||
outputHandlers = new PriorityQueue<PacketOutputHandler>(10, new Comparator<PacketOutputHandler>() {
|
||||
@Override
|
||||
public int compare(PacketOutputHandler o1, PacketOutputHandler o2) {
|
||||
return Ints.compare(o1.getPriority().getSlot(), o2.getPriority().getSlot());
|
||||
}
|
||||
});
|
||||
}
|
||||
return outputHandlers.add(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a given output handler from the serialization queue.
|
||||
* <p>
|
||||
* This can only be invoked on server side packet events.
|
||||
* @param handler - the handler to remove.
|
||||
* @return TRUE if the handler was removed, FALSE otherwise.
|
||||
*/
|
||||
public boolean removeOutputHandler(@Nonnull PacketOutputHandler handler) {
|
||||
checkServerSide();
|
||||
Preconditions.checkNotNull(handler, "handler cannot be NULL.");
|
||||
|
||||
if (outputHandlers != null) {
|
||||
return outputHandlers.remove(handler);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every registered output handler in no particular order.
|
||||
* @return Every registered output handler.
|
||||
*/
|
||||
@Nonnull
|
||||
public Collection<PacketOutputHandler> getOutputHandlers() {
|
||||
if (outputHandlers != null) {
|
||||
return outputHandlers;
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener that is invoked after a packet has been successfully sent to the client, or received
|
||||
* by the server.
|
||||
* <p>
|
||||
* Received packets are not guarenteed to have been fully processed, but packets passed
|
||||
* to {@link ProtocolManager#recieveClientPacket(Player, PacketContainer)} will be processed after the
|
||||
* current packet event.
|
||||
* <p>
|
||||
* Note that post listeners will be executed asynchronously off the main thread. They are not executed
|
||||
* in any defined order.
|
||||
* @param listener - the listener that will be invoked.
|
||||
* @return TRUE if it was added.
|
||||
*/
|
||||
public boolean addPostListener(PacketPostListener listener) {
|
||||
if (postListeners == null)
|
||||
postListeners = Lists.newArrayList();
|
||||
return postListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the first instance of the given listener.
|
||||
* @param listener - listener to remove.
|
||||
* @return TRUE if it was removed, FALSE otherwise.
|
||||
*/
|
||||
public boolean removePostListener(PacketPostListener listener) {
|
||||
if (postListeners != null) {
|
||||
return postListeners.remove(listener);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an immutable view of all the listeners that will be invoked once the packet has been sent or received.
|
||||
* @return Every post packet listener. Never NULL.
|
||||
*/
|
||||
public List<PacketPostListener> getPostListeners() {
|
||||
return postListeners != null ? Collections.unmodifiableList(postListeners) : Collections.<PacketPostListener>emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of packets that will be schedule (in-order) when the current packet has been successfully transmitted.
|
||||
* <p>
|
||||
* This list is modifiable.
|
||||
* @return List of packets that will be scheduled.
|
||||
*/
|
||||
public List<ScheduledPacket> getScheduledPackets() {
|
||||
if (scheduledPackets == null)
|
||||
scheduledPackets = Lists.newArrayList();
|
||||
return scheduledPackets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the packet event is server side.
|
||||
*/
|
||||
private void checkServerSide() {
|
||||
if (side.isForClient()) {
|
||||
throw new IllegalStateException("Must be a server side packet.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a byte buffer without the header in the current packet.
|
||||
* <p>
|
||||
* It's safe to modify the position of the buffer.
|
||||
* @param buffer - a read-only byte source.
|
||||
* @return A byte buffer without the header in the current packet.
|
||||
* @throws IOException If integer reading fails
|
||||
*/
|
||||
protected ByteBuffer skipHeader(ByteBuffer buffer) throws IOException {
|
||||
skipHeader(new DataInputStream(new ByteBufferInputStream(buffer)));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an input stream without the header in the current packet.
|
||||
* <p>
|
||||
* It's safe to modify the input stream.
|
||||
* @param input - input stream
|
||||
* @return An input stream without the header
|
||||
* @throws IOException If integer reading fails
|
||||
*/
|
||||
protected abstract DataInputStream skipHeader(DataInputStream input) throws IOException;
|
||||
|
||||
/**
|
||||
* Return the byte buffer prepended with the packet header.
|
||||
* @param buffer - the read-only byte buffer.
|
||||
* @param type - the current packet.
|
||||
* @return The byte buffer.
|
||||
*/
|
||||
protected abstract ByteBuffer addHeader(ByteBuffer buffer, PacketType type);
|
||||
|
||||
/**
|
||||
* Return the input stream prepended with the packet header.
|
||||
* @param input - the input stream.
|
||||
* @param type - the current packet.
|
||||
* @return The byte buffer.
|
||||
*/
|
||||
protected abstract DataInputStream addHeader(DataInputStream input, PacketType type);
|
||||
|
||||
/**
|
||||
* Determine if the given marker has any output handlers.
|
||||
* @param marker - the marker to check.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public static boolean hasOutputHandlers(NetworkMarker marker) {
|
||||
return marker != null && !marker.getOutputHandlers().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given marker has any post listeners.
|
||||
*
|
||||
* @param marker - the marker to check.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public static boolean hasPostListeners(NetworkMarker marker) {
|
||||
return marker != null && !marker.getPostListeners().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the byte buffer stored in the given marker.
|
||||
* @param marker - the marker.
|
||||
* @return The byte buffer, or NULL if not found.
|
||||
*/
|
||||
public static byte[] getByteBuffer(NetworkMarker marker) {
|
||||
if (marker != null) {
|
||||
ByteBuffer buffer = marker.getInputBuffer();
|
||||
|
||||
if (buffer != null) {
|
||||
byte[] data = new byte[buffer.remaining()];
|
||||
|
||||
buffer.get(data, 0, data.length);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the network marker of a particular event without creating it.
|
||||
* <p>
|
||||
* This is an internal method that should not be used by API users.
|
||||
*
|
||||
* @param event - the event.
|
||||
* @return The network marker.
|
||||
*/
|
||||
public static NetworkMarker getNetworkMarker(PacketEvent event) {
|
||||
return event.networkMarker;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the scheduled packets of a particular network marker without constructing the list.
|
||||
* <p>
|
||||
* This is an internal method that should not be used by API users.
|
||||
*
|
||||
* @param marker - the marker.
|
||||
* @return The list, or NULL if not found or initialized.
|
||||
*/
|
||||
public static List<ScheduledPacket> readScheduledPackets(NetworkMarker marker) {
|
||||
public static Deque<ScheduledPacket> readScheduledPackets(NetworkMarker marker) {
|
||||
return marker.scheduledPackets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether or not this marker belongs to a client or a server side packet.
|
||||
*
|
||||
* @return The side the parent packet belongs to.
|
||||
*/
|
||||
public ConnectionSide getSide() {
|
||||
return this.side;
|
||||
}
|
||||
|
||||
public PacketType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener that is invoked after a packet has been successfully sent to the client, or received by the server.
|
||||
* <p>
|
||||
* Received packets are not guarenteed to have been fully processed, but packets passed to {@link
|
||||
* ProtocolManager#receiveClientPacket(Player, PacketContainer)} will be processed after the current packet event.
|
||||
* <p>
|
||||
* Note that post listeners will be executed asynchronously off the main thread. They are not executed in any defined
|
||||
* order.
|
||||
*
|
||||
* @param listener - the listener that will be invoked.
|
||||
* @return TRUE if it was added.
|
||||
*/
|
||||
public boolean addPostListener(PacketPostListener listener) {
|
||||
if (this.postListeners == null) {
|
||||
this.postListeners = new HashSet<>();
|
||||
}
|
||||
|
||||
return this.postListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the first instance of the given listener.
|
||||
*
|
||||
* @param listener - listener to remove.
|
||||
* @return TRUE if it was removed, FALSE otherwise.
|
||||
*/
|
||||
public boolean removePostListener(PacketPostListener listener) {
|
||||
if (this.postListeners != null) {
|
||||
return this.postListeners.remove(listener);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an immutable view of all the listeners that will be invoked once the packet has been sent or received.
|
||||
*
|
||||
* @return Every post packet listener. Never NULL.
|
||||
*/
|
||||
public Set<PacketPostListener> getPostListeners() {
|
||||
return this.postListeners != null ? this.postListeners : Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a modifiable queue of packets that will be schedule (in-order) when the current packet has been
|
||||
* successfully transmitted.
|
||||
*
|
||||
* @return the queue of packets to schedule after this packet, in order.
|
||||
*/
|
||||
public Deque<ScheduledPacket> getScheduledPackets() {
|
||||
if (this.scheduledPackets == null) {
|
||||
this.scheduledPackets = new LinkedList<>();
|
||||
}
|
||||
|
||||
return this.scheduledPackets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the packet event is server side.
|
||||
*/
|
||||
private void checkServerSide() {
|
||||
if (this.side.isForClient()) {
|
||||
throw new IllegalStateException("Must be a server side packet.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,15 +17,13 @@
|
||||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
@@ -34,69 +32,80 @@ import org.bukkit.plugin.Plugin;
|
||||
* Represents a packet listener with useful constructors.
|
||||
* <p>
|
||||
* Remember to override onPacketReceiving() and onPacketSending(), depending on the ConnectionSide.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class PacketAdapter implements PacketListener {
|
||||
|
||||
protected Plugin plugin;
|
||||
protected ConnectionSide connectionSide;
|
||||
protected ListeningWhitelist receivingWhitelist = ListeningWhitelist.EMPTY_WHITELIST;
|
||||
protected ListeningWhitelist sendingWhitelist = ListeningWhitelist.EMPTY_WHITELIST;
|
||||
|
||||
/**
|
||||
* Initialize a packet adapter using a collection of parameters. Use {@link #params()} to get an instance to this builder.
|
||||
* Initialize a packet adapter using a collection of parameters. Use {@link #params()} to get an instance to this
|
||||
* builder.
|
||||
*
|
||||
* @param params - the parameters.
|
||||
*/
|
||||
public PacketAdapter(@Nonnull AdapterParameteters params) {
|
||||
this(
|
||||
checkValidity(params).plugin, params.connectionSide, params.listenerPriority,
|
||||
params.gamePhase, params.options, params.packets
|
||||
checkValidity(params).plugin, params.connectionSide, params.listenerPriority,
|
||||
params.gamePhase, params.options, params.packets
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize a packet listener with the given parameters.
|
||||
*
|
||||
* @param plugin - the plugin.
|
||||
* @param types - the packet types.
|
||||
* @param types - the packet types.
|
||||
*/
|
||||
public PacketAdapter(Plugin plugin, PacketType... types) {
|
||||
this(plugin, ListenerPriority.NORMAL, types);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize a packet listener with the given parameters.
|
||||
*
|
||||
* @param plugin - the plugin.
|
||||
* @param types - the packet types.
|
||||
* @param types - the packet types.
|
||||
*/
|
||||
public PacketAdapter(Plugin plugin, Iterable<? extends PacketType> types) {
|
||||
this(params(plugin, Iterables.toArray(types, PacketType.class)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize a packet listener with the given parameters.
|
||||
* @param plugin - the plugin.
|
||||
*
|
||||
* @param plugin - the plugin.
|
||||
* @param listenerPriority - the priority.
|
||||
* @param types - the packet types.
|
||||
* @param types - the packet types.
|
||||
*/
|
||||
public PacketAdapter(Plugin plugin, ListenerPriority listenerPriority, Iterable<? extends PacketType> types) {
|
||||
this(params(plugin, Iterables.toArray(types, PacketType.class)).listenerPriority(listenerPriority));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize a packet listener with the given parameters.
|
||||
* @param plugin - the plugin.
|
||||
*
|
||||
* @param plugin - the plugin.
|
||||
* @param listenerPriority - the priority.
|
||||
* @param types - the packet types.
|
||||
* @param options - the options.
|
||||
* @param types - the packet types.
|
||||
* @param options - the options.
|
||||
*/
|
||||
public PacketAdapter(Plugin plugin, ListenerPriority listenerPriority, Iterable<? extends PacketType> types, ListenerOptions... options) {
|
||||
this(params(plugin, Iterables.toArray(types, PacketType.class)).listenerPriority(listenerPriority).options(options));
|
||||
public PacketAdapter(Plugin plugin, ListenerPriority listenerPriority, Iterable<? extends PacketType> types,
|
||||
ListenerOptions... options) {
|
||||
this(
|
||||
params(plugin, Iterables.toArray(types, PacketType.class)).listenerPriority(listenerPriority).options(options));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize a packet listener with the given parameters.
|
||||
* @param plugin - the plugin.
|
||||
*
|
||||
* @param plugin - the plugin.
|
||||
* @param listenerPriority - the priority.
|
||||
* @param types - the packet types.
|
||||
* @param types - the packet types.
|
||||
*/
|
||||
public PacketAdapter(Plugin plugin, ListenerPriority listenerPriority, PacketType... types) {
|
||||
this(params(plugin, types).listenerPriority(listenerPriority));
|
||||
@@ -106,102 +115,69 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
private PacketAdapter(
|
||||
Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority,
|
||||
GamePhase gamePhase, ListenerOptions[] options, PacketType... packets) {
|
||||
|
||||
if (plugin == null)
|
||||
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("plugin cannot be null");
|
||||
if (connectionSide == null)
|
||||
throw new IllegalArgumentException("connectionSide cannot be null");
|
||||
if (listenerPriority == null)
|
||||
throw new IllegalArgumentException("listenerPriority cannot be null");
|
||||
if (gamePhase == null)
|
||||
throw new IllegalArgumentException("gamePhase cannot be NULL");
|
||||
if (packets == null)
|
||||
throw new IllegalArgumentException("packets cannot be null");
|
||||
if (options == null)
|
||||
throw new IllegalArgumentException("options cannot be null");
|
||||
|
||||
ListenerOptions[] serverOptions = options;
|
||||
ListenerOptions[] clientOptions = options;
|
||||
|
||||
// Special case that allows us to specify optionIntercept().
|
||||
if (connectionSide == ConnectionSide.BOTH) {
|
||||
serverOptions = except(serverOptions, new ListenerOptions[0],
|
||||
ListenerOptions.INTERCEPT_INPUT_BUFFER);
|
||||
}
|
||||
|
||||
if (connectionSide == null) {
|
||||
throw new IllegalArgumentException("connectionSide cannot be null");
|
||||
}
|
||||
if (listenerPriority == null) {
|
||||
throw new IllegalArgumentException("listenerPriority cannot be null");
|
||||
}
|
||||
if (gamePhase == null) {
|
||||
throw new IllegalArgumentException("gamePhase cannot be NULL");
|
||||
}
|
||||
if (packets == null) {
|
||||
throw new IllegalArgumentException("packets cannot be null");
|
||||
}
|
||||
if (options == null) {
|
||||
throw new IllegalArgumentException("options cannot be null");
|
||||
}
|
||||
|
||||
// Add whitelists
|
||||
if (connectionSide.isForServer())
|
||||
sendingWhitelist = ListeningWhitelist.newBuilder().
|
||||
priority(listenerPriority).
|
||||
types(packets).
|
||||
gamePhase(gamePhase).
|
||||
options(serverOptions).
|
||||
build();
|
||||
|
||||
if (connectionSide.isForClient())
|
||||
receivingWhitelist = ListeningWhitelist.newBuilder().
|
||||
priority(listenerPriority).
|
||||
types(packets).
|
||||
gamePhase(gamePhase).
|
||||
options(clientOptions).
|
||||
build();
|
||||
|
||||
if (connectionSide.isForServer()) {
|
||||
sendingWhitelist = ListeningWhitelist.newBuilder()
|
||||
.priority(listenerPriority)
|
||||
.types(packets)
|
||||
.gamePhase(gamePhase)
|
||||
.options(options)
|
||||
.build();
|
||||
}
|
||||
|
||||
if (connectionSide.isForClient()) {
|
||||
receivingWhitelist = ListeningWhitelist.newBuilder()
|
||||
.priority(listenerPriority)
|
||||
.types(packets)
|
||||
.gamePhase(gamePhase)
|
||||
.options(options)
|
||||
.build();
|
||||
}
|
||||
|
||||
this.plugin = plugin;
|
||||
this.connectionSide = connectionSide;
|
||||
}
|
||||
|
||||
// Remove a given element from an array
|
||||
private static <T> T[] except(T[] values, T[] buffer, T except) {
|
||||
List<T> result = Lists.newArrayList(values);
|
||||
|
||||
result.remove(except);
|
||||
return result.toArray(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
// Lets prevent some bugs
|
||||
throw new IllegalStateException("Override onPacketReceiving to get notifcations of received packets!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
// Lets prevent some bugs
|
||||
throw new IllegalStateException("Override onPacketSending to get notifcations of sent packets!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getReceivingWhitelist() {
|
||||
return receivingWhitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getSendingWhitelist() {
|
||||
return sendingWhitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the name of the plugin that has been associated with the listener.
|
||||
*
|
||||
* @param listener - the listener.
|
||||
* @return Name of the associated plugin.
|
||||
*/
|
||||
public static String getPluginName(PacketListener listener) {
|
||||
return getPluginName(listener.getPlugin());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the name of the given plugin.
|
||||
*
|
||||
* @param plugin - the plugin.
|
||||
* @return Name of the given plugin.
|
||||
*/
|
||||
public static String getPluginName(Plugin plugin) {
|
||||
if (plugin == null)
|
||||
if (plugin == null) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
try {
|
||||
return plugin.getName();
|
||||
@@ -209,20 +185,12 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
return plugin.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// This is used by the error reporter
|
||||
return String.format("PacketAdapter[plugin=%s, sending=%s, receiving=%s]",
|
||||
getPluginName(this),
|
||||
sendingWhitelist,
|
||||
receivingWhitelist);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a helper object for passing parameters to the packet adapter.
|
||||
* <p>
|
||||
* This is often simpler and better than passing them directly to each constructor.
|
||||
*
|
||||
* @return Helper object.
|
||||
*/
|
||||
public static AdapterParameteters params() {
|
||||
@@ -233,32 +201,91 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
* Construct a helper object for passing parameters to the packet adapter.
|
||||
* <p>
|
||||
* This is often simpler and better than passing them directly to each constructor.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
*
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param packets - the packet types the listener is looking for.
|
||||
* @return Helper object.
|
||||
*/
|
||||
public static AdapterParameteters params(Plugin plugin, PacketType... packets) {
|
||||
return new AdapterParameteters().plugin(plugin).types(packets);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the required parameters are set.
|
||||
*/
|
||||
private static AdapterParameteters checkValidity(AdapterParameteters params) {
|
||||
if (params == null) {
|
||||
throw new IllegalArgumentException("params cannot be NULL.");
|
||||
}
|
||||
if (params.plugin == null) {
|
||||
throw new IllegalStateException("Plugin was never set in the parameters.");
|
||||
}
|
||||
if (params.connectionSide == null) {
|
||||
throw new IllegalStateException("Connection side was never set in the parameters.");
|
||||
}
|
||||
if (params.packets == null) {
|
||||
throw new IllegalStateException("Packet IDs was never set in the parameters.");
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
// Lets prevent some bugs
|
||||
throw new IllegalStateException("Override onPacketReceiving to get notifcations of received packets!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
// Lets prevent some bugs
|
||||
throw new IllegalStateException("Override onPacketSending to get notifcations of sent packets!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getReceivingWhitelist() {
|
||||
return receivingWhitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getSendingWhitelist() {
|
||||
return sendingWhitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// This is used by the error reporter
|
||||
return String.format("PacketAdapter[plugin=%s, sending=%s, receiving=%s]",
|
||||
getPluginName(this),
|
||||
sendingWhitelist,
|
||||
receivingWhitelist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a builder for passing parameters to the packet adapter constructor.
|
||||
* <p>
|
||||
* Note: Never make spelling mistakes in a public API!
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class AdapterParameteters {
|
||||
|
||||
private Plugin plugin;
|
||||
private ConnectionSide connectionSide;
|
||||
private PacketType[] packets;
|
||||
|
||||
|
||||
// Parameters with default values
|
||||
private GamePhase gamePhase = GamePhase.PLAYING;
|
||||
private ListenerOptions[] options = new ListenerOptions[0];
|
||||
private ListenerPriority listenerPriority = ListenerPriority.NORMAL;
|
||||
|
||||
|
||||
/**
|
||||
* Set the plugin that spawned this listener. This parameter is required.
|
||||
*
|
||||
* @param plugin - the plugin.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -266,9 +293,10 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
this.plugin = Preconditions.checkNotNull(plugin, "plugin cannot be NULL.");
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the packet types this listener is looking for. This parameter is required.
|
||||
*
|
||||
* @param connectionSide - the new packet type.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -276,27 +304,30 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
this.connectionSide = Preconditions.checkNotNull(connectionSide, "connectionside cannot be NULL.");
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set this adapter to also look for client-side packets.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public AdapterParameteters clientSide() {
|
||||
return connectionSide(ConnectionSide.add(connectionSide, ConnectionSide.CLIENT_SIDE));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set this adapter to also look for server-side packets.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public AdapterParameteters serverSide() {
|
||||
return connectionSide(ConnectionSide.add(connectionSide, ConnectionSide.SERVER_SIDE));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the the event priority, where the execution is in ascending order from lowest to highest.
|
||||
* <p>
|
||||
* Default is {@link ListenerPriority#NORMAL}.
|
||||
*
|
||||
* @param listenerPriority - the new event priority.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -304,11 +335,13 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
this.listenerPriority = Preconditions.checkNotNull(listenerPriority, "listener priority cannot be NULL.");
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set which game phase this listener is active under. This is a hint for ProtocolLib to start intercepting login packets.
|
||||
* Set which game phase this listener is active under. This is a hint for ProtocolLib to start intercepting login
|
||||
* packets.
|
||||
* <p>
|
||||
* Default is {@link GamePhase#PLAYING}, which will not intercept login packets.
|
||||
*
|
||||
* @param gamePhase - the new game phase.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -316,19 +349,22 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
this.gamePhase = Preconditions.checkNotNull(gamePhase, "gamePhase cannot be NULL.");
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the game phase to {@link GamePhase#LOGIN}, allowing ProtocolLib to intercept login packets.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public AdapterParameteters loginPhase() {
|
||||
return gamePhase(GamePhase.LOGIN);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set listener options that decide whether or not to intercept the raw packet data as read from the network stream.
|
||||
* Set listener options that decide whether or not to intercept the raw packet data as read from the network
|
||||
* stream.
|
||||
* <p>
|
||||
* The default is to disable this raw packet interception.
|
||||
*
|
||||
* @param options - every option to use.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -338,9 +374,11 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set listener options that decide whether or not to intercept the raw packet data as read from the network stream.
|
||||
* Set listener options that decide whether or not to intercept the raw packet data as read from the network
|
||||
* stream.
|
||||
* <p>
|
||||
* The default is to disable this raw packet interception.
|
||||
*
|
||||
* @param options - every option to use.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -349,9 +387,10 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
this.options = options.toArray(new ListenerOptions[0]);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a given option to the current builder.
|
||||
*
|
||||
* @param option - the option to add.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -359,34 +398,38 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
if (options == null) {
|
||||
return options(option);
|
||||
} else {
|
||||
Set<ListenerOptions> current = Sets.newHashSet(options);
|
||||
final Set<ListenerOptions> current = Sets.newHashSet(options);
|
||||
current.add(option);
|
||||
return options(current);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener option to {@link ListenerOptions#INTERCEPT_INPUT_BUFFER}, causing ProtocolLib to read the raw packet data from the network stream.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public AdapterParameteters optionIntercept() {
|
||||
return addOption(ListenerOptions.INTERCEPT_INPUT_BUFFER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener option to {@link ListenerOptions#ASYNC}, indicating that our listener is thread safe.
|
||||
* Set the listener option to {@link ListenerOptions#ASYNC}, indicating that our outbound listener is thread safe.
|
||||
* <p>
|
||||
* This allows ProtocolLib to perform certain optimizations.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public AdapterParameteters optionAsync() {
|
||||
return addOption(ListenerOptions.ASYNC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener option to {@link ListenerOptions#SYNC}, indicating that our inbound listener must be executed
|
||||
* on the main server thread.
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public AdapterParameteters optionSync() {
|
||||
return addOption(ListenerOptions.SYNC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packet types the listener is looking for.
|
||||
* <p>
|
||||
* This parameter is required.
|
||||
*
|
||||
* @param packets - the packet types to look for.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -398,16 +441,18 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
}
|
||||
}
|
||||
this.packets = Preconditions.checkNotNull(packets, "packets cannot be NULL");
|
||||
|
||||
if (packets.length == 0)
|
||||
|
||||
if (packets.length == 0) {
|
||||
throw new IllegalArgumentException("Passed an empty packet type array.");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the packet types the listener is looking for.
|
||||
* <p>
|
||||
* This parameter is required.
|
||||
*
|
||||
* @param packets - a set of packet types to look for.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -415,19 +460,4 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
return types(packets.toArray(new PacketType[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the required parameters are set.
|
||||
*/
|
||||
private static AdapterParameteters checkValidity(AdapterParameteters params) {
|
||||
if (params == null)
|
||||
throw new IllegalArgumentException("params cannot be NULL.");
|
||||
if (params.plugin == null)
|
||||
throw new IllegalStateException("Plugin was never set in the parameters.");
|
||||
if (params.connectionSide == null)
|
||||
throw new IllegalStateException("Connection side was never set in the parameters.");
|
||||
if (params.packets == null)
|
||||
throw new IllegalStateException("Packet IDs was never set in the parameters.");
|
||||
return params;
|
||||
}
|
||||
}
|
||||
@@ -21,61 +21,49 @@ import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.injector.StructureCache;
|
||||
import com.comphenix.protocol.reflect.*;
|
||||
import com.comphenix.protocol.reflect.cloning.*;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.ObjectWriter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.cloning.AggregateCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters;
|
||||
import com.comphenix.protocol.reflect.cloning.BukkitCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.Cloner;
|
||||
import com.comphenix.protocol.reflect.cloning.CollectionCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.FieldCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.GuavaOptionalCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.ImmutableDetector;
|
||||
import com.comphenix.protocol.reflect.cloning.JavaOptionalCloner;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.reflect.instances.MinecraftGenerator;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.StreamSerializer;
|
||||
import com.comphenix.protocol.wrappers.*;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.*;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtBase;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.comphenix.protocol.wrappers.Converters;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.WorldType;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.MerchantRecipe;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.util.Vector;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a Minecraft packet indirectly.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@@ -85,8 +73,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
private PacketType type;
|
||||
|
||||
// Support for serialization
|
||||
private static ConcurrentMap<Class<?>, Method> writeMethods = Maps.newConcurrentMap();
|
||||
private static ConcurrentMap<Class<?>, Method> readMethods = Maps.newConcurrentMap();
|
||||
private static final Map<PacketType, Function<Object, Object>> PACKET_DESERIALIZER_METHODS = new ConcurrentHashMap<>();
|
||||
|
||||
// Used to clone packets
|
||||
private static final AggregateCloner DEEP_CLONER = AggregateCloner
|
||||
@@ -99,7 +86,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
.andThen(CollectionCloner.class)
|
||||
.andThen(getSpecializedDeepClonerFactory())
|
||||
.build();
|
||||
|
||||
|
||||
private static final AggregateCloner SHALLOW_CLONER = AggregateCloner
|
||||
.newBuilder()
|
||||
.instanceProvider(StructureCache::newPacket)
|
||||
@@ -113,7 +100,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
}};
|
||||
})
|
||||
.build();
|
||||
|
||||
|
||||
// Packets that cannot be cloned by our default deep cloner
|
||||
private static final Set<PacketType> FAST_CLONE_UNSUPPORTED = Sets.newHashSet(
|
||||
PacketType.Play.Server.BOSS,
|
||||
@@ -129,7 +116,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
public PacketContainer(PacketType type) {
|
||||
this(type, StructureCache.newPacket(type));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a packet container for an existing packet.
|
||||
* @param type - Type of the given packet.
|
||||
@@ -138,7 +125,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
public PacketContainer(PacketType type, Object handle) {
|
||||
this(type, handle, StructureCache.getStructure(type).withTarget(handle));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a packet container for an existing packet.
|
||||
* @param type - Type of the given packet.
|
||||
@@ -147,7 +134,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
*/
|
||||
public PacketContainer(PacketType type, Object handle, StructureModifier<Object> structure) {
|
||||
super(handle, structure);
|
||||
|
||||
|
||||
this.type = type;
|
||||
|
||||
setDefaults();
|
||||
@@ -170,13 +157,13 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
PacketType type = PacketType.fromClass(packet.getClass());
|
||||
return new PacketContainer(type, packet);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For serialization.
|
||||
*/
|
||||
protected PacketContainer() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the underlying Minecraft packet.
|
||||
* @return Underlying Minecraft packet.
|
||||
@@ -184,7 +171,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
public Object getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the generic structure modifier for this packet.
|
||||
* @return Structure modifier.
|
||||
@@ -216,20 +203,20 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
public PacketType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a shallow copy of the current packet.
|
||||
* <p>
|
||||
* This merely writes the content of each field to the new class directly,
|
||||
* without performing any expensive copies.
|
||||
*
|
||||
*
|
||||
* @return A shallow copy of the current packet.
|
||||
*/
|
||||
public PacketContainer shallowClone() {
|
||||
Object clonedPacket = SHALLOW_CLONER.clone(getHandle());
|
||||
return new PacketContainer(getType(), clonedPacket);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a deep copy of the current packet.
|
||||
* <p>
|
||||
@@ -237,32 +224,40 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
* known immutable objects and primitive types.
|
||||
* <p>
|
||||
* Note that the inflated buffers in packet 51 and 56 will be copied directly to save memory.
|
||||
*
|
||||
*
|
||||
* @return A deep copy of the current packet.
|
||||
*/
|
||||
public PacketContainer deepClone() {
|
||||
Object clonedPacket = null;
|
||||
Object handle = this.getHandle();
|
||||
PacketType packetType = this.getType();
|
||||
if (handle == null || packetType == null) {
|
||||
// nothing to clone, just carry on (this should normally not happen)
|
||||
return this;
|
||||
}
|
||||
|
||||
if (!FAST_CLONE_UNSUPPORTED.contains(type)) {
|
||||
// try fast cloning first
|
||||
if (!FAST_CLONE_UNSUPPORTED.contains(packetType)) {
|
||||
try {
|
||||
clonedPacket = DEEP_CLONER.clone(getHandle());
|
||||
Object cloned = DEEP_CLONER.clone(handle);
|
||||
return new PacketContainer(packetType, cloned);
|
||||
} catch (Exception ex) {
|
||||
FAST_CLONE_UNSUPPORTED.add(type);
|
||||
FAST_CLONE_UNSUPPORTED.add(packetType);
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back on the slower alternative method of reading and writing back the packet
|
||||
if (clonedPacket == null) {
|
||||
clonedPacket = SerializableCloner.clone(this).getHandle();
|
||||
}
|
||||
Object serialized = this.serializeToBuffer();
|
||||
Object deserialized = deserializeFromBuffer(packetType, serialized);
|
||||
|
||||
return new PacketContainer(getType(), clonedPacket);
|
||||
// ensure that we don't leak memory
|
||||
ReferenceCountUtil.safeRelease(serialized);
|
||||
return new PacketContainer(packetType, deserialized);
|
||||
}
|
||||
|
||||
|
||||
// To save space, we'll skip copying the inflated buffers in packet 51 and 56
|
||||
private static Function<BuilderParameters, Cloner> getSpecializedDeepClonerFactory() {
|
||||
private static com.google.common.base.Function<BuilderParameters, Cloner> getSpecializedDeepClonerFactory() {
|
||||
// Look at what you've made me do Java, look at it!!
|
||||
return new Function<BuilderParameters, Cloner>() {
|
||||
return new com.google.common.base.Function<BuilderParameters, Cloner>() {
|
||||
@Override
|
||||
public Cloner apply(@Nullable BuilderParameters param) {
|
||||
return new FieldCloner(param.getAggregateCloner(), param.getInstanceProvider()) {{
|
||||
@@ -281,97 +276,124 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private void writeObject(ObjectOutputStream output) throws IOException {
|
||||
// Default serialization
|
||||
// Default serialization
|
||||
output.defaultWriteObject();
|
||||
|
||||
// We'll take care of NULL packets as well
|
||||
output.writeBoolean(handle != null);
|
||||
|
||||
try {
|
||||
ByteBuf buffer = createPacketBuffer();
|
||||
MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, buffer);
|
||||
|
||||
// serialize the packet
|
||||
ByteBuf buffer = (ByteBuf) this.serializeToBuffer();
|
||||
if (buffer != null) {
|
||||
output.writeBoolean(true);
|
||||
output.writeInt(buffer.readableBytes());
|
||||
buffer.readBytes(output, buffer.readableBytes());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Minecraft packet doesn't support DataOutputStream", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Insufficient security privileges.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IOException("Could not serialize Minecraft packet.", e);
|
||||
|
||||
// ensure that we don't leak memory
|
||||
ReferenceCountUtil.safeRelease(buffer);
|
||||
} else {
|
||||
output.writeBoolean(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
|
||||
// Default deserialization
|
||||
// Default deserialization
|
||||
input.defaultReadObject();
|
||||
|
||||
// Get structure modifier
|
||||
structureModifier = StructureCache.getStructure(type);
|
||||
|
||||
// Don't read NULL packets
|
||||
if (input.readBoolean()) {
|
||||
ByteBuf buffer = createPacketBuffer();
|
||||
buffer.writeBytes(input, input.readInt());
|
||||
|
||||
// Create a default instance of the packet
|
||||
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
||||
Object serializer = MinecraftReflection.getPacketDataSerializer(buffer);
|
||||
// Deserialize the packet from the stream (if present)
|
||||
this.structureModifier = StructureCache.getStructure(this.type);
|
||||
if (input.readBoolean()) {
|
||||
int dataLength = input.readInt();
|
||||
|
||||
try {
|
||||
handle = type.getPacketClass()
|
||||
.getConstructor(MinecraftReflection.getPacketDataSerializerClass())
|
||||
.newInstance(serializer);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
// they might have a static method to create them instead
|
||||
Method method = FuzzyReflection.fromClass(type.getPacketClass(), true)
|
||||
.getMethod(FuzzyMethodContract
|
||||
.newBuilder()
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.returnTypeExact(type.getPacketClass())
|
||||
.parameterExactArray(MinecraftReflection.getPacketDataSerializerClass())
|
||||
.build());
|
||||
try {
|
||||
handle = method.invoke(null, serializer);
|
||||
} catch (ReflectiveOperationException ignored) {
|
||||
throw new RuntimeException("Failed to construct packet for " + type, ex);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handle = StructureCache.newPacket(type);
|
||||
ByteBuf byteBuf = (ByteBuf) MinecraftReflection.createPacketDataSerializer(dataLength);
|
||||
while (true) {
|
||||
// ObjectInputStream only reads a specific amount of bytes before moving the cursor forwards and
|
||||
// allows reading the next byte chunk. So we need to read until the data is gone from the stream and
|
||||
// fully transferred into the buffer.
|
||||
int transferredBytes = byteBuf.writeBytes(input, dataLength);
|
||||
|
||||
// Call the read method
|
||||
try {
|
||||
MinecraftMethods.getPacketReadByteBufMethod().invoke(handle, buffer);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Minecraft packet doesn't support DataInputStream", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Insufficient security privileges.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IOException("Could not deserialize Minecraft packet.", e);
|
||||
// check if we reached the end of the stream, or if the stream has no more data available
|
||||
dataLength -= transferredBytes;
|
||||
if (dataLength <= 0 || transferredBytes <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// And we're done
|
||||
structureModifier = structureModifier.withTarget(handle);
|
||||
}
|
||||
|
||||
// deserialize & ensure that we don't leak memory
|
||||
Object packet = deserializeFromBuffer(this.type, byteBuf);
|
||||
ReferenceCountUtil.safeRelease(byteBuf);
|
||||
|
||||
this.handle = packet;
|
||||
this.structureModifier = this.structureModifier.withTarget(packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new packet data serializer.
|
||||
* @return The packet data serializer.
|
||||
* @deprecated use {@link MinecraftReflection#createPacketDataSerializer(int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static ByteBuf createPacketBuffer() {
|
||||
ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.buffer();
|
||||
Class<?> packetSerializer = MinecraftReflection.getPacketDataSerializerClass();
|
||||
return (ByteBuf) MinecraftReflection.createPacketDataSerializer(0);
|
||||
}
|
||||
|
||||
try {
|
||||
return (ByteBuf) packetSerializer.getConstructor(ByteBuf.class).newInstance(buffer);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot construct packet serializer.", e);
|
||||
// ---- Cloning
|
||||
|
||||
public static Object deserializeFromBuffer(PacketType packetType, Object buffer) {
|
||||
if (buffer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Function<Object, Object> deserializer = PACKET_DESERIALIZER_METHODS.computeIfAbsent(packetType, type -> {
|
||||
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
||||
// best guess - a constructor which takes a buffer as the only argument
|
||||
ConstructorAccessor bufferConstructor = Accessors.getConstructorAccessorOrNull(
|
||||
type.getPacketClass(),
|
||||
MinecraftReflection.getPacketDataSerializerClass());
|
||||
if (bufferConstructor != null) {
|
||||
return bufferConstructor::invoke;
|
||||
}
|
||||
|
||||
// they might have a static method to create them instead
|
||||
List<Method> methods = FuzzyReflection.fromClass(type.getPacketClass(), true)
|
||||
.getMethodList(FuzzyMethodContract.newBuilder()
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.returnTypeExact(type.getPacketClass())
|
||||
.parameterExactArray(MinecraftReflection.getPacketDataSerializerClass())
|
||||
.build());
|
||||
if (!methods.isEmpty()) {
|
||||
MethodAccessor accessor = Accessors.getMethodAccessor(methods.get(0));
|
||||
return buf -> accessor.invoke(null, buf);
|
||||
}
|
||||
}
|
||||
|
||||
// try to construct a packet instance using a no-args constructor and invoke the read method
|
||||
MethodAccessor readMethod = MinecraftMethods.getPacketReadByteBufMethod();
|
||||
Objects.requireNonNull(readMethod,
|
||||
"Unable to find the Packet#read(ByteBuf) method, cannot deserialize " + type);
|
||||
|
||||
Object checkInstance = DefaultInstances.DEFAULT.create(type.getPacketClass());
|
||||
Objects.requireNonNull(checkInstance, "Unable to construct empty packet, cannot deserialize " + type);
|
||||
|
||||
// okay, Packet#read exists
|
||||
return buf -> {
|
||||
Object packet = DefaultInstances.DEFAULT.create(type.getPacketClass());
|
||||
readMethod.invoke(packet, buf);
|
||||
return packet;
|
||||
};
|
||||
});
|
||||
return deserializer.apply(buffer);
|
||||
}
|
||||
|
||||
public Object serializeToBuffer() {
|
||||
Object handle = this.getHandle();
|
||||
if (handle == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object targetBuffer = MinecraftReflection.createPacketDataSerializer(0);
|
||||
MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, targetBuffer);
|
||||
return targetBuffer;
|
||||
}
|
||||
|
||||
// ---- Metadata
|
||||
@@ -419,7 +441,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
private Method getMethodLazily(ConcurrentMap<Class<?>, Method> lookup,
|
||||
Class<?> handleClass, String methodName, Class<?> parameterClass) {
|
||||
Method method = lookup.get(handleClass);
|
||||
|
||||
|
||||
// Atomic operation
|
||||
if (method == null) {
|
||||
Method initialized = FuzzyReflection.fromClass(handleClass).getMethod(
|
||||
@@ -429,13 +451,13 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
||||
returnTypeVoid().
|
||||
build());
|
||||
method = lookup.putIfAbsent(handleClass, initialized);
|
||||
|
||||
|
||||
// Use our version if we succeeded
|
||||
if (method == null) {
|
||||
method = initialized;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,58 +23,56 @@ import java.io.ObjectOutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.EventObject;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.async.AsyncMarker;
|
||||
import com.comphenix.protocol.error.PluginContext;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayer;
|
||||
import com.comphenix.protocol.injector.temporary.TemporaryPlayer;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
|
||||
/**
|
||||
* Represents a packet sending or receiving event. Changes to the packet will be
|
||||
* reflected in the final version to be sent or received. It is also possible to cancel an event.
|
||||
* Represents a packet sending or receiving event. Changes to the packet will be reflected in the final version to be
|
||||
* sent or received. It is also possible to cancel an event.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PacketEvent extends EventObject implements Cancellable {
|
||||
|
||||
public static final ReportType REPORT_CHANGING_PACKET_TYPE_IS_CONFUSING = new ReportType(
|
||||
"Plugin %s changed packet type from %s to %s in packet listener. This is confusing for other plugins! (Not an error, though!)");
|
||||
|
||||
|
||||
private static final SetMultimap<PacketType, PacketType> CHANGE_WARNINGS =
|
||||
Multimaps.synchronizedSetMultimap(HashMultimap.create());
|
||||
|
||||
|
||||
/**
|
||||
* Automatically generated by Eclipse.
|
||||
*/
|
||||
private static final long serialVersionUID = -5360289379097430620L;
|
||||
|
||||
// Network input and output handlers
|
||||
NetworkMarker networkMarker;
|
||||
private transient WeakReference<Player> playerReference;
|
||||
|
||||
private PacketContainer packet;
|
||||
private boolean serverPacket;
|
||||
private boolean cancel;
|
||||
|
||||
private AsyncMarker asyncMarker;
|
||||
private boolean asynchronous;
|
||||
|
||||
// Network input and output handlers
|
||||
NetworkMarker networkMarker;
|
||||
|
||||
// Whether or not a packet event is read only
|
||||
private boolean readOnly;
|
||||
private boolean filtered;
|
||||
|
||||
|
||||
/**
|
||||
* Use the static constructors to create instances of this event.
|
||||
*
|
||||
* @param source - the event source.
|
||||
*/
|
||||
public PacketEvent(Object source) {
|
||||
@@ -85,8 +83,9 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
private PacketEvent(Object source, PacketContainer packet, Player player, boolean serverPacket) {
|
||||
this(source, packet, null, player, serverPacket, true);
|
||||
}
|
||||
|
||||
private PacketEvent(Object source, PacketContainer packet, NetworkMarker marker, Player player, boolean serverPacket, boolean filtered) {
|
||||
|
||||
private PacketEvent(Object source, PacketContainer packet, NetworkMarker marker, Player player, boolean serverPacket,
|
||||
boolean filtered) {
|
||||
super(source);
|
||||
this.packet = packet;
|
||||
this.playerReference = new WeakReference<>(player);
|
||||
@@ -94,7 +93,7 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
this.serverPacket = serverPacket;
|
||||
this.filtered = filtered;
|
||||
}
|
||||
|
||||
|
||||
private PacketEvent(PacketEvent origial, AsyncMarker asyncMarker) {
|
||||
super(origial.source);
|
||||
this.packet = origial.packet;
|
||||
@@ -109,6 +108,7 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
|
||||
/**
|
||||
* Creates an event representing a client packet transmission.
|
||||
*
|
||||
* @param source - the event source.
|
||||
* @param packet - the packet.
|
||||
* @param client - the client that sent the packet.
|
||||
@@ -117,9 +117,10 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
public static PacketEvent fromClient(Object source, PacketContainer packet, Player client) {
|
||||
return new PacketEvent(source, packet, client, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an event representing a client packet transmission.
|
||||
*
|
||||
* @param source - the event source.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the network marker.
|
||||
@@ -129,85 +130,94 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
public static PacketEvent fromClient(Object source, PacketContainer packet, NetworkMarker marker, Player client) {
|
||||
return new PacketEvent(source, packet, marker, client, false, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an event representing a client packet transmission.
|
||||
* <p>
|
||||
* If <i>filtered</i> is FALSE, then this event is only processed by packet monitors.
|
||||
* @param source - the event source.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the network marker.
|
||||
* @param client - the client that sent the packet.
|
||||
*
|
||||
* @param source - the event source.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the network marker.
|
||||
* @param client - the client that sent the packet.
|
||||
* @param filtered - whether or not this packet event is processed by every packet listener.
|
||||
* @return The event.
|
||||
*/
|
||||
public static PacketEvent fromClient(Object source, PacketContainer packet, NetworkMarker marker, Player client, boolean filtered) {
|
||||
public static PacketEvent fromClient(Object source, PacketContainer packet, NetworkMarker marker, Player client,
|
||||
boolean filtered) {
|
||||
return new PacketEvent(source, packet, marker, client, false, filtered);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an event representing a server packet transmission.
|
||||
* @param source - the event source.
|
||||
* @param packet - the packet.
|
||||
*
|
||||
* @param source - the event source.
|
||||
* @param packet - the packet.
|
||||
* @param recipient - the client that will receieve the packet.
|
||||
* @return The event.
|
||||
*/
|
||||
public static PacketEvent fromServer(Object source, PacketContainer packet, Player recipient) {
|
||||
public static PacketEvent fromServer(Object source, PacketContainer packet, Player recipient) {
|
||||
return new PacketEvent(source, packet, recipient, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an event representing a server packet transmission.
|
||||
* @param source - the event source.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the network marker.
|
||||
*
|
||||
* @param source - the event source.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the network marker.
|
||||
* @param recipient - the client that will receieve the packet.
|
||||
* @return The event.
|
||||
*/
|
||||
public static PacketEvent fromServer(Object source, PacketContainer packet, NetworkMarker marker, Player recipient) {
|
||||
return new PacketEvent(source, packet, marker, recipient, true, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an event representing a server packet transmission.
|
||||
* <p>
|
||||
* If <i>filtered</i> is FALSE, then this event is only processed by packet monitors.
|
||||
* @param source - the event source.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the network marker.
|
||||
*
|
||||
* @param source - the event source.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the network marker.
|
||||
* @param recipient - the client that will receieve the packet.
|
||||
* @param filtered - whether or not this packet event is processed by every packet listener.
|
||||
* @param filtered - whether or not this packet event is processed by every packet listener.
|
||||
* @return The event.
|
||||
*/
|
||||
public static PacketEvent fromServer(Object source, PacketContainer packet, NetworkMarker marker, Player recipient, boolean filtered) {
|
||||
public static PacketEvent fromServer(Object source, PacketContainer packet, NetworkMarker marker, Player recipient,
|
||||
boolean filtered) {
|
||||
return new PacketEvent(source, packet, marker, recipient, true, filtered);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an asynchronous packet event from a synchronous event and a async marker.
|
||||
* @param event - the original synchronous event.
|
||||
*
|
||||
* @param event - the original synchronous event.
|
||||
* @param marker - the asynchronous marker.
|
||||
* @return The new packet event.
|
||||
*/
|
||||
public static PacketEvent fromSynchronous(PacketEvent event, AsyncMarker marker) {
|
||||
return new PacketEvent(event, marker);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if we are executing the packet event in an asynchronous thread.
|
||||
* <p>
|
||||
* If so, you must synchronize all calls to the Bukkit API.
|
||||
* <p>
|
||||
* Generally, most server packets are executed on the main thread, whereas client packets
|
||||
* are all executed asynchronously.
|
||||
* Generally, most server packets are executed on the main thread, whereas client packets are all executed
|
||||
* asynchronously.
|
||||
*
|
||||
* @return TRUE if we are, FALSE otherwise.
|
||||
*/
|
||||
public boolean isAsync() {
|
||||
return !Bukkit.isPrimaryThread();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the packet that will be sent to the player.
|
||||
*
|
||||
* @return Packet to send to the player.
|
||||
*/
|
||||
public PacketContainer getPacket() {
|
||||
@@ -216,14 +226,17 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
|
||||
/**
|
||||
* Replace the packet that will be sent to the player.
|
||||
*
|
||||
* @param packet - the packet that will be sent instead.
|
||||
*/
|
||||
public void setPacket(PacketContainer packet) {
|
||||
if (readOnly)
|
||||
if (readOnly) {
|
||||
throw new IllegalStateException("The packet event is read-only.");
|
||||
if (packet == null)
|
||||
}
|
||||
if (packet == null) {
|
||||
throw new IllegalArgumentException("Cannot set packet to NULL. Use setCancelled() instead.");
|
||||
|
||||
}
|
||||
|
||||
// Change warnings
|
||||
final PacketType oldType = this.packet.getType();
|
||||
final PacketType newType = packet.getType();
|
||||
@@ -232,53 +245,77 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
if (CHANGE_WARNINGS.put(oldType, newType)) {
|
||||
ProtocolLibrary.getErrorReporter().reportWarning(this,
|
||||
Report.newBuilder(REPORT_CHANGING_PACKET_TYPE_IS_CONFUSING).
|
||||
messageParam(PluginContext.getPluginCaller(new Exception()), oldType, newType).
|
||||
build());
|
||||
messageParam(PluginContext.getPluginCaller(new Exception()), oldType, newType).
|
||||
build());
|
||||
}
|
||||
}
|
||||
this.packet = packet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the packet ID.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getPacketType()} instead.
|
||||
*
|
||||
* @return The current packet ID.
|
||||
*/
|
||||
@Deprecated
|
||||
public int getPacketID() {
|
||||
return packet.getId();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the packet type.
|
||||
*
|
||||
* @return The type.
|
||||
*/
|
||||
public PacketType getPacketType() {
|
||||
return packet.getType();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves whether or not the packet should be cancelled.
|
||||
*
|
||||
* @return TRUE if it should be cancelled, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether or not the packet should be cancelled. Uncancelling is possible.
|
||||
* <p>
|
||||
* <b>Warning</b>: A cancelled packet should never be re-transmitted. Use the asynchronous
|
||||
* packet manager if you need to perform extensive processing. It should also be used if you need to synchronize with
|
||||
* the main thread.
|
||||
* <p>
|
||||
* This ensures that other plugins can work with the same packet.
|
||||
* <p>
|
||||
* An asynchronous listener can also delay a packet indefinitely without having to block its thread.
|
||||
*
|
||||
* @param cancel - TRUE if it should be cancelled, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
if (readOnly) {
|
||||
throw new IllegalStateException("The packet event is read-only.");
|
||||
}
|
||||
this.cancel = cancel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the object responsible for managing the serialized input and output of a packet.
|
||||
* <p>
|
||||
* Note that the serialized input data is only available for client-side packets, and the output handlers
|
||||
* can only be applied to server-side packets.
|
||||
* Note that the serialized input data is only available for client-side packets, and the output handlers can only be
|
||||
* applied to server-side packets.
|
||||
*
|
||||
* @return The network manager.
|
||||
*/
|
||||
public NetworkMarker getNetworkMarker() {
|
||||
if (networkMarker == null) {
|
||||
if (isServerPacket()) {
|
||||
networkMarker = new NetworkMarker.EmptyBufferMarker(
|
||||
serverPacket ? ConnectionSide.SERVER_SIDE : ConnectionSide.CLIENT_SIDE);
|
||||
networkMarker = new NetworkMarker(serverPacket ? ConnectionSide.SERVER_SIDE : ConnectionSide.CLIENT_SIDE, this.getPacketType());
|
||||
} else {
|
||||
throw new IllegalStateException("Add the option ListenerOptions.INTERCEPT_INPUT_BUFFER to your listener.");
|
||||
}
|
||||
@@ -290,31 +327,12 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
* Update the network manager.
|
||||
* <p>
|
||||
* This method is internal - do not call.
|
||||
*
|
||||
* @param networkMarker - the new network manager.
|
||||
*/
|
||||
public void setNetworkMarker(NetworkMarker networkMarker) {
|
||||
this.networkMarker = Preconditions.checkNotNull(networkMarker, "marker cannot be NULL");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not the packet should be cancelled. Uncancelling is possible.
|
||||
* <p>
|
||||
* <b>Warning</b>: A cancelled packet should never be re-transmitted. Use the asynchronous
|
||||
* packet manager if you need to perform extensive processing. It should also be used
|
||||
* if you need to synchronize with the main thread.
|
||||
* <p>
|
||||
* This ensures that other plugins can work with the same packet.
|
||||
* <p>
|
||||
* An asynchronous listener can also delay a packet indefinitely without having to block its thread.
|
||||
*
|
||||
* @param cancel - TRUE if it should be cancelled, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
if (readOnly)
|
||||
throw new IllegalStateException("The packet event is read-only.");
|
||||
this.cancel = cancel;
|
||||
}
|
||||
|
||||
private WeakReference<Player> getPlayerReference() {
|
||||
Player player = playerReference.get();
|
||||
@@ -332,6 +350,7 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
|
||||
/**
|
||||
* Retrieves the player that has sent the packet or is receiving it.
|
||||
*
|
||||
* @return The player associated with this event.
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
@@ -350,7 +369,7 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
* <li>kickPlayer</li>
|
||||
* <li>isOnline</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Anything else will throw an UnsupportedOperationException. Use this check before calling other methods when
|
||||
* dealing with packets early in the login sequence or if you get the aforementioned exception.
|
||||
*
|
||||
@@ -359,27 +378,29 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
public boolean isPlayerTemporary() {
|
||||
return getPlayer() instanceof TemporaryPlayer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if this packet is filtered by every packet listener.
|
||||
* <p>
|
||||
* If not, it will only be intercepted by monitor packets.
|
||||
*
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean isFiltered() {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether or not this packet was created by the server.
|
||||
* <p>
|
||||
* Most listeners can deduce this by noting which listener method was invoked.
|
||||
*
|
||||
* @return TRUE if the packet was created by the server, FALSE if it was created by a client.
|
||||
*/
|
||||
public boolean isServerPacket() {
|
||||
return serverPacket;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the asynchronous marker.
|
||||
* <p>
|
||||
@@ -387,54 +408,62 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
* asynchronous event, the marker is used to correctly pass the packet around to the different threads.
|
||||
* <p>
|
||||
* Note that if there are no asynchronous events that can receive this packet, the marker is NULL.
|
||||
*
|
||||
* @return The current asynchronous marker, or NULL.
|
||||
*/
|
||||
public AsyncMarker getAsyncMarker() {
|
||||
return asyncMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the asynchronous marker.
|
||||
* <p>
|
||||
* If the marker is non-null at the end of an synchronous event processing, the packet will be scheduled
|
||||
* to be processed asynchronously with the given settings.
|
||||
* If the marker is non-null at the end of an synchronous event processing, the packet will be scheduled to be
|
||||
* processed asynchronously with the given settings.
|
||||
* <p>
|
||||
* Note that if there are no asynchronous events that can receive this packet, the marker should be NULL.
|
||||
*
|
||||
* @param asyncMarker - the new asynchronous marker, or NULL.
|
||||
* @throws IllegalStateException If the current event is asynchronous.
|
||||
*/
|
||||
public void setAsyncMarker(AsyncMarker asyncMarker) {
|
||||
if (isAsynchronous())
|
||||
if (isAsynchronous()) {
|
||||
throw new IllegalStateException("The marker is immutable for asynchronous events");
|
||||
if (readOnly)
|
||||
}
|
||||
if (readOnly) {
|
||||
throw new IllegalStateException("The packet event is read-only.");
|
||||
}
|
||||
this.asyncMarker = asyncMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current packet event is read only.
|
||||
* <p>
|
||||
* This is used to ensure that a monitor listener doesn't accidentally alter the state of the event. However,
|
||||
* it is still possible to modify the packet itself, as it would require too many resources to verify its integrity.
|
||||
* This is used to ensure that a monitor listener doesn't accidentally alter the state of the event. However, it is
|
||||
* still possible to modify the packet itself, as it would require too many resources to verify its integrity.
|
||||
* <p>
|
||||
* Thus, the packet is considered immutable if the packet event is read only.
|
||||
*
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the read-only state of this packet event.
|
||||
* <p>
|
||||
* This will be reset for every packet listener.
|
||||
*
|
||||
* @param readOnly - TRUE if it is read-only, FALSE otherwise.
|
||||
*/
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the packet event has been executed asynchronously or not.
|
||||
*
|
||||
* @return TRUE if this packet event is asynchronous, FALSE otherwise.
|
||||
*/
|
||||
public boolean isAsynchronous() {
|
||||
@@ -445,14 +474,16 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
* Schedule a packet for sending or receiving after the current packet event is successful.
|
||||
* <p>
|
||||
* The packet will be added to {@link NetworkMarker#getScheduledPackets()}.
|
||||
*
|
||||
* @param scheduled - the packet to transmit or receive.
|
||||
*/
|
||||
public void schedule(ScheduledPacket scheduled) {
|
||||
getNetworkMarker().getScheduledPackets().add(scheduled);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unschedule a specific packet.
|
||||
*
|
||||
* @param scheduled - the scheduled packet.
|
||||
* @return TRUE if it was unscheduled, FALSE otherwise.
|
||||
*/
|
||||
@@ -462,9 +493,9 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private void writeObject(ObjectOutputStream output) throws IOException {
|
||||
// Default serialization
|
||||
// Default serialization
|
||||
output.defaultWriteObject();
|
||||
|
||||
// Write the name of the player (or NULL if it's not set)
|
||||
@@ -473,17 +504,17 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
|
||||
// Default deserialization
|
||||
// Default deserialization
|
||||
input.defaultReadObject();
|
||||
|
||||
final SerializedOfflinePlayer serialized = (SerializedOfflinePlayer) input.readObject();
|
||||
|
||||
|
||||
// Better than nothing
|
||||
if (serialized != null) {
|
||||
// Store it, to prevent weak reference from cleaning up the reference
|
||||
Player offlinePlayer = serialized.getPlayer();
|
||||
playerReference = new WeakReference<>(offlinePlayer);
|
||||
}
|
||||
if (serialized != null) {
|
||||
// Store it, to prevent weak reference from cleaning up the reference
|
||||
Player offlinePlayer = serialized.getPlayer();
|
||||
playerReference = new WeakReference<>(offlinePlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,49 +23,53 @@ import org.bukkit.plugin.Plugin;
|
||||
* Represents a listener that receives notifications when packets are sending or being received.
|
||||
* <p>
|
||||
* Use {@link PacketAdapter} for a simple wrapper around this interface.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface PacketListener {
|
||||
|
||||
|
||||
/**
|
||||
* Invoked right before a packet is transmitted from the server to the client.
|
||||
* <p>
|
||||
* Note that the packet may be replaced, if needed.
|
||||
* <p>
|
||||
* This method is executed on the main thread in 1.6.4 and earlier, and thus the Bukkit API is safe to use.
|
||||
* <p>
|
||||
* In Minecraft 1.7.2 and later, this method MAY be executed asynchronously, but only if {@link ListenerOptions#ASYNC}
|
||||
* have been specified in the listener. This is off by default.
|
||||
* This method is executed on the main server thread by default. However, some spigot forks (like paper) schedule
|
||||
* specific packets off the main thread. If the {@link ListenerOptions#ASYNC} option is not specified any invocation
|
||||
* of this method will be on the main server thread.
|
||||
*
|
||||
* @param event - the packet that should be sent.
|
||||
*/
|
||||
public void onPacketSending(PacketEvent event);
|
||||
void onPacketSending(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Invoked right before a received packet from a client is being processed.
|
||||
* <p>
|
||||
* <b>WARNING</b>: <br>
|
||||
* This method will be called <i>asynchronously</i>! You should synchronize with the main
|
||||
* thread using {@link org.bukkit.scheduler.BukkitScheduler#scheduleSyncDelayedTask(Plugin, Runnable, long) scheduleSyncDelayedTask}
|
||||
* if you need to call the Bukkit API.
|
||||
* This method will be called asynchronously (or on the netty event loop) by default. If the
|
||||
* {@link ListenerOptions#SYNC} option is specified, the invocation of this method will be synced to the main server
|
||||
* thread which might cause issues due to delayed packets.
|
||||
*
|
||||
* @param event - the packet that has been received.
|
||||
*/
|
||||
public void onPacketReceiving(PacketEvent event);
|
||||
|
||||
void onPacketReceiving(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Retrieve which packets sent by the server this listener will observe.
|
||||
*
|
||||
* @return List of server packets to observe, along with the priority.
|
||||
*/
|
||||
public ListeningWhitelist getSendingWhitelist();
|
||||
|
||||
ListeningWhitelist getSendingWhitelist();
|
||||
|
||||
/**
|
||||
* Retrieve which packets sent by the client this listener will observe.
|
||||
*
|
||||
* @return List of server packets to observe, along with the priority.
|
||||
*/
|
||||
public ListeningWhitelist getReceivingWhitelist();
|
||||
|
||||
ListeningWhitelist getReceivingWhitelist();
|
||||
|
||||
/**
|
||||
* Retrieve the plugin that created list packet listener.
|
||||
*
|
||||
* @return The plugin, or NULL if not available.
|
||||
*/
|
||||
public Plugin getPlugin();
|
||||
Plugin getPlugin();
|
||||
}
|
||||
@@ -1,142 +1,143 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2017 dmulloy2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import org.apache.commons.lang.Validate;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Stores and retrieves metadata for applicable packet objects.
|
||||
* @author dmulloy2
|
||||
*/
|
||||
class PacketMetadata {
|
||||
|
||||
private static class MetaObject<T> {
|
||||
private final String key;
|
||||
private final T value;
|
||||
|
||||
private MetaObject(String key, T value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
|
||||
if (o instanceof MetaObject) {
|
||||
MetaObject that = (MetaObject) o;
|
||||
return that.key.equals(this.key) &&
|
||||
that.value.equals(this.value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MetaObject[" + key + "=" + value + "]";
|
||||
}
|
||||
}
|
||||
|
||||
// Packet meta cache
|
||||
private static Cache<Object, List<MetaObject>> META_CACHE;
|
||||
|
||||
public static <T> Optional<T> get(Object packet, String key) {
|
||||
Validate.notNull(key, "Null keys are not permitted!");
|
||||
|
||||
if (META_CACHE == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<MetaObject> meta = META_CACHE.getIfPresent(packet);
|
||||
if (meta == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
for (MetaObject object : meta) {
|
||||
if (object.key.equals(key)) {
|
||||
return Optional.of((T) object.value);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static void createCache() {
|
||||
META_CACHE = CacheBuilder
|
||||
.newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static <T> void set(Object packet, String key, T value) {
|
||||
Validate.notNull(key, "Null keys are not permitted!");
|
||||
|
||||
if (META_CACHE == null) {
|
||||
createCache();
|
||||
}
|
||||
|
||||
List<MetaObject> packetMeta;
|
||||
|
||||
try {
|
||||
packetMeta = META_CACHE.get(packet, ArrayList::new);
|
||||
} catch (ExecutionException ex) {
|
||||
// Not possible, but let's humor the array list constructor having an issue
|
||||
packetMeta = new ArrayList<>();
|
||||
}
|
||||
|
||||
packetMeta.removeIf(meta -> meta.key.equals(key));
|
||||
packetMeta.add(new MetaObject<>(key, value));
|
||||
META_CACHE.put(packet, packetMeta);
|
||||
}
|
||||
|
||||
public static <T> Optional<T> remove(Object packet, String key) {
|
||||
Validate.notNull(key, "Null keys are not permitted!");
|
||||
|
||||
if (META_CACHE == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<MetaObject> packetMeta = META_CACHE.getIfPresent(packet);
|
||||
if (packetMeta == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Optional<T> value = Optional.empty();
|
||||
Iterator<MetaObject> iter = packetMeta.iterator();
|
||||
while (iter.hasNext()) {
|
||||
MetaObject meta = iter.next();
|
||||
if (meta.key.equals(key)) {
|
||||
value = Optional.of((T) meta.value);
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2017 dmulloy2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
|
||||
/**
|
||||
* Stores and retrieves metadata for applicable packet objects.
|
||||
* @author dmulloy2
|
||||
*/
|
||||
class PacketMetadata {
|
||||
|
||||
private static class MetaObject<T> {
|
||||
private final String key;
|
||||
private final T value;
|
||||
|
||||
private MetaObject(String key, T value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
|
||||
if (o instanceof MetaObject) {
|
||||
MetaObject that = (MetaObject) o;
|
||||
return that.key.equals(this.key) &&
|
||||
that.value.equals(this.value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MetaObject[" + key + "=" + value + "]";
|
||||
}
|
||||
}
|
||||
|
||||
// Packet meta cache
|
||||
private static Cache<Object, List<MetaObject>> META_CACHE;
|
||||
|
||||
public static <T> Optional<T> get(Object packet, String key) {
|
||||
Validate.notNull(key, "Null keys are not permitted!");
|
||||
|
||||
if (META_CACHE == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<MetaObject> meta = META_CACHE.getIfPresent(packet);
|
||||
if (meta == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
for (MetaObject object : meta) {
|
||||
if (object.key.equals(key)) {
|
||||
return Optional.of((T) object.value);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static void createCache() {
|
||||
META_CACHE = CacheBuilder
|
||||
.newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static <T> void set(Object packet, String key, T value) {
|
||||
Validate.notNull(key, "Null keys are not permitted!");
|
||||
|
||||
if (META_CACHE == null) {
|
||||
createCache();
|
||||
}
|
||||
|
||||
List<MetaObject> packetMeta;
|
||||
|
||||
try {
|
||||
packetMeta = META_CACHE.get(packet, ArrayList::new);
|
||||
} catch (ExecutionException ex) {
|
||||
// Not possible, but let's humor the array list constructor having an issue
|
||||
packetMeta = new ArrayList<>();
|
||||
}
|
||||
|
||||
packetMeta.removeIf(meta -> meta.key.equals(key));
|
||||
packetMeta.add(new MetaObject<>(key, value));
|
||||
META_CACHE.put(packet, packetMeta);
|
||||
}
|
||||
|
||||
public static <T> Optional<T> remove(Object packet, String key) {
|
||||
Validate.notNull(key, "Null keys are not permitted!");
|
||||
|
||||
if (META_CACHE == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<MetaObject> packetMeta = META_CACHE.getIfPresent(packet);
|
||||
if (packetMeta == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Optional<T> value = Optional.empty();
|
||||
Iterator<MetaObject> iter = packetMeta.iterator();
|
||||
while (iter.hasNext()) {
|
||||
MetaObject meta = iter.next();
|
||||
if (meta.key.equals(key)) {
|
||||
value = Optional.of((T) meta.value);
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents an adapter version of a post listener.
|
||||
* @author Kristian
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketStream;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents a packet that is scheduled for transmission at a later stage.
|
||||
* @author Kristian
|
||||
@@ -121,17 +119,11 @@ public class ScheduledPacket {
|
||||
*/
|
||||
public void schedule(PacketStream stream) {
|
||||
Preconditions.checkNotNull(stream, "stream cannot be NULL");
|
||||
|
||||
try {
|
||||
if (getSender() == Sender.CLIENT) {
|
||||
stream.recieveClientPacket(getTarget(), getPacket(), isFiltered());
|
||||
} else {
|
||||
stream.sendServerPacket(getTarget(), getPacket(), isFiltered());
|
||||
}
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Cannot send packet " + this + " to " + stream);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot send packet " + this + " to " + stream);
|
||||
|
||||
if (getSender() == Sender.CLIENT) {
|
||||
stream.receiveClientPacket(getTarget(), getPacket(), isFiltered());
|
||||
} else {
|
||||
stream.sendServerPacket(getTarget(), getPacket(), isFiltered());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,16 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.ByteBuddyFactory;
|
||||
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.profile.PlayerProfile;
|
||||
|
||||
import net.bytebuddy.description.ByteCodeElement;
|
||||
import net.bytebuddy.description.modifier.Visibility;
|
||||
@@ -36,18 +45,13 @@ import net.bytebuddy.implementation.FieldAccessor;
|
||||
import net.bytebuddy.implementation.InvocationHandlerAdapter;
|
||||
import net.bytebuddy.implementation.MethodCall;
|
||||
import net.bytebuddy.implementation.MethodDelegation;
|
||||
import net.bytebuddy.implementation.bind.annotation.AllArguments;
|
||||
import net.bytebuddy.implementation.bind.annotation.Origin;
|
||||
import net.bytebuddy.implementation.bind.annotation.FieldValue;
|
||||
import net.bytebuddy.implementation.bind.annotation.Pipe;
|
||||
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.utility.ByteBuddyFactory;
|
||||
|
||||
/**
|
||||
* Represents a player object that can be serialized by Java.
|
||||
*
|
||||
@@ -162,6 +166,11 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
||||
|
||||
public void setStatistic(Statistic statistic, EntityType entityType, int i) { }
|
||||
|
||||
@Override
|
||||
public Location getLastDeathLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFirstPlayed() {
|
||||
return firstPlayed;
|
||||
@@ -176,7 +185,12 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
||||
public UUID getUniqueId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PlayerProfile getPlayerProfile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -278,18 +292,33 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
||||
for (int idx = 0; idx < offlinePlayerMethods.length; ++idx)
|
||||
methodNames[idx] = offlinePlayerMethods[idx].getName();
|
||||
|
||||
final Map<Method, MethodAccessor> accessorCache = new ConcurrentHashMap<>();
|
||||
final ElementMatcher.Junction<ByteCodeElement> forwardedMethods = ElementMatchers.namedOneOf(methodNames);
|
||||
|
||||
try {
|
||||
final MethodDelegation forwarding = MethodDelegation.withDefaultConfiguration()
|
||||
.withBinders(Pipe.Binder.install(Function.class))
|
||||
.to(new Object() {
|
||||
@RuntimeType
|
||||
public Object intercept(@Pipe Function<OfflinePlayer, Object> pipe,
|
||||
@FieldValue("offlinePlayer") OfflinePlayer proxy) {
|
||||
return pipe.apply(proxy);
|
||||
final MethodDelegation forwarding = MethodDelegation.withDefaultConfiguration().to(new Object() {
|
||||
@RuntimeType
|
||||
public Object intercept(
|
||||
@Origin Method calledMethod,
|
||||
@AllArguments Object[] args,
|
||||
@FieldValue("offlinePlayer") OfflinePlayer proxy
|
||||
) {
|
||||
MethodAccessor accessor = accessorCache.computeIfAbsent(calledMethod, method -> {
|
||||
// special case - some methods (like getName) are defined in OfflinePlayer as well
|
||||
// as the online Player class. This causes cast exceptions if we try to invoke the method on
|
||||
// the online player with our proxy. Prevent that
|
||||
if (OfflinePlayer.class.isAssignableFrom(method.getDeclaringClass())) {
|
||||
return Accessors.getMethodAccessor(
|
||||
OfflinePlayer.class,
|
||||
method.getName(),
|
||||
method.getParameterTypes());
|
||||
} else {
|
||||
return Accessors.getMethodAccessor(method);
|
||||
}
|
||||
});
|
||||
return accessor.invoke(proxy, args);
|
||||
}
|
||||
});
|
||||
|
||||
final InvocationHandlerAdapter throwException = InvocationHandlerAdapter.of((obj, method, args) -> {
|
||||
throw new UnsupportedOperationException(
|
||||
|
||||
@@ -17,25 +17,23 @@
|
||||
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.PacketConstructor.Unwrapper;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents an object capable of converting wrapped Bukkit objects into NMS objects.
|
||||
@@ -45,123 +43,136 @@ import com.google.common.primitives.Primitives;
|
||||
* <li>org.bukkit.entity.Player to net.minecraft.server.EntityPlayer</li>
|
||||
* <li>org.bukkit.World to net.minecraft.server.WorldServer</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class BukkitUnwrapper implements Unwrapper {
|
||||
private static BukkitUnwrapper DEFAULT;
|
||||
|
||||
public static final ReportType REPORT_ILLEGAL_ARGUMENT = new ReportType("Illegal argument.");
|
||||
public static final ReportType REPORT_SECURITY_LIMITATION = new ReportType("Security limitation.");
|
||||
public static final ReportType REPORT_CANNOT_FIND_UNWRAP_METHOD = new ReportType("Cannot find method.");
|
||||
|
||||
public static final ReportType REPORT_CANNOT_READ_FIELD_HANDLE = new ReportType("Cannot read field 'handle'.");
|
||||
|
||||
private static Map<Class<?>, Unwrapper> unwrapperCache = new ConcurrentHashMap<Class<?>, Unwrapper>();
|
||||
|
||||
|
||||
private static final Map<Class<?>, Unwrapper> UNWRAPPER_CACHE = new ConcurrentHashMap<Class<?>, Unwrapper>();
|
||||
private static BukkitUnwrapper DEFAULT;
|
||||
|
||||
// The current error reporter
|
||||
private final ErrorReporter reporter;
|
||||
|
||||
/**
|
||||
* Retrieve the default instance of the Bukkit unwrapper.
|
||||
* @return The default instance.
|
||||
*/
|
||||
public static BukkitUnwrapper getInstance() {
|
||||
ErrorReporter currentReporter = ProtocolLibrary.getErrorReporter();
|
||||
|
||||
// Also recreate the unwrapper if the error reporter has changed
|
||||
if (DEFAULT == null || DEFAULT.reporter != currentReporter) {
|
||||
DEFAULT = new BukkitUnwrapper(currentReporter);
|
||||
}
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new Bukkit unwrapper with ProtocolLib's default error reporter.
|
||||
*/
|
||||
public BukkitUnwrapper() {
|
||||
this(ProtocolLibrary.getErrorReporter());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new Bukkit unwrapper with the given error reporter.
|
||||
*
|
||||
* @param reporter - the error reporter to use.
|
||||
*/
|
||||
public BukkitUnwrapper(ErrorReporter reporter) {
|
||||
this.reporter = reporter;
|
||||
this.reporter = reporter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the default instance of the Bukkit unwrapper.
|
||||
*
|
||||
* @return The default instance.
|
||||
*/
|
||||
public static BukkitUnwrapper getInstance() {
|
||||
ErrorReporter currentReporter = ProtocolLibrary.getErrorReporter();
|
||||
|
||||
// Also recreate the unwrapper if the error reporter has changed
|
||||
if (DEFAULT == null || DEFAULT.reporter != currentReporter) {
|
||||
DEFAULT = new BukkitUnwrapper(currentReporter);
|
||||
}
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
private static Class<?> checkClass(Class<?> input, Class<?> expected, Class<?> result) {
|
||||
if (expected.isAssignableFrom(input)) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object unwrapItem(Object wrappedObject) {
|
||||
// Special case
|
||||
if (wrappedObject == null)
|
||||
if (wrappedObject == null) {
|
||||
return null;
|
||||
}
|
||||
Class<?> currentClass = PacketConstructor.getClass(wrappedObject);
|
||||
|
||||
|
||||
// No need to unwrap primitives
|
||||
if (currentClass.isPrimitive() || currentClass.equals(String.class))
|
||||
if (currentClass.isPrimitive() || currentClass.equals(String.class)) {
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
// Next, check for types that doesn't have a getHandle()
|
||||
if (wrappedObject instanceof Collection) {
|
||||
return handleCollection((Collection<Object>) wrappedObject);
|
||||
} else if (Primitives.isWrapperType(currentClass) || wrappedObject instanceof String) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Unwrapper specificUnwrapper = getSpecificUnwrapper(currentClass);
|
||||
|
||||
|
||||
// Retrieve the handle
|
||||
if (specificUnwrapper != null)
|
||||
if (specificUnwrapper != null) {
|
||||
return specificUnwrapper.unwrapItem(wrappedObject);
|
||||
else
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle a collection of items
|
||||
private Object handleCollection(Collection<Object> wrappedObject) {
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Object> copy = DefaultInstances.DEFAULT.getDefault(wrappedObject.getClass());
|
||||
|
||||
|
||||
if (copy != null) {
|
||||
// Unwrap every element
|
||||
for (Object element : wrappedObject) {
|
||||
copy.add(unwrapItem(element));
|
||||
}
|
||||
return copy;
|
||||
|
||||
|
||||
} else {
|
||||
// Impossible
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a cached class unwrapper for the given class.
|
||||
*
|
||||
* @param type - the type of the class.
|
||||
* @return An unwrapper for the given class.
|
||||
*/
|
||||
private Unwrapper getSpecificUnwrapper(final Class<?> type) {
|
||||
// See if we're already determined this
|
||||
if (unwrapperCache.containsKey(type)) {
|
||||
if (UNWRAPPER_CACHE.containsKey(type)) {
|
||||
// We will never remove from the cache, so this ought to be thread safe
|
||||
return unwrapperCache.get(type);
|
||||
return UNWRAPPER_CACHE.get(type);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
final Method find = type.getMethod("getHandle");
|
||||
|
||||
|
||||
// It's thread safe, as getMethod should return the same handle
|
||||
Unwrapper methodUnwrapper = new Unwrapper() {
|
||||
@Override
|
||||
public Object unwrapItem(Object wrappedObject) {
|
||||
try {
|
||||
if (wrappedObject instanceof Class)
|
||||
if (wrappedObject instanceof Class) {
|
||||
return checkClass((Class<?>) wrappedObject, type, find.getReturnType());
|
||||
}
|
||||
return find.invoke(wrappedObject);
|
||||
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_ILLEGAL_ARGUMENT).error(e).callerParam(wrappedObject, find)
|
||||
@@ -173,14 +184,14 @@ public class BukkitUnwrapper implements Unwrapper {
|
||||
// This is really bad
|
||||
throw new RuntimeException("Minecraft error.", e);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
unwrapperCache.put(type, methodUnwrapper);
|
||||
|
||||
UNWRAPPER_CACHE.put(type, methodUnwrapper);
|
||||
return methodUnwrapper;
|
||||
|
||||
|
||||
} catch (SecurityException e) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_SECURITY_LIMITATION).error(e).callerParam(type)
|
||||
@@ -188,16 +199,18 @@ public class BukkitUnwrapper implements Unwrapper {
|
||||
} catch (NoSuchMethodException e) {
|
||||
// Maybe it's a proxy?
|
||||
Unwrapper proxyUnwrapper = getProxyUnwrapper(type);
|
||||
if (proxyUnwrapper != null)
|
||||
if (proxyUnwrapper != null) {
|
||||
return proxyUnwrapper;
|
||||
}
|
||||
|
||||
// Try getting the field unwrapper too
|
||||
Unwrapper fieldUnwrapper = getFieldUnwrapper(type);
|
||||
if (fieldUnwrapper != null)
|
||||
if (fieldUnwrapper != null) {
|
||||
return fieldUnwrapper;
|
||||
else
|
||||
} else {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_FIND_UNWRAP_METHOD).error(e).callerParam(type));
|
||||
}
|
||||
}
|
||||
|
||||
// Default method
|
||||
@@ -211,22 +224,19 @@ public class BukkitUnwrapper implements Unwrapper {
|
||||
if (Player.class.isAssignableFrom(type)) {
|
||||
final Method getHandle = MinecraftReflection.getCraftPlayerClass().getMethod("getHandle");
|
||||
|
||||
Unwrapper unwrapper = new Unwrapper() {
|
||||
@Override
|
||||
public Object unwrapItem(Object wrapped) {
|
||||
Unwrapper unwrapper = wrapped -> {
|
||||
try {
|
||||
return getHandle.invoke(((Player) wrapped).getPlayer());
|
||||
} catch (Throwable ex) {
|
||||
try {
|
||||
return getHandle.invoke(((Player) wrapped).getPlayer());
|
||||
} catch (Throwable ex) {
|
||||
try {
|
||||
return getHandle.invoke(Bukkit.getPlayer(((Player) wrapped).getUniqueId()));
|
||||
} catch (ReflectiveOperationException ex1) {
|
||||
throw new RuntimeException("Failed to unwrap proxy " + wrapped, ex);
|
||||
}
|
||||
return getHandle.invoke(Bukkit.getPlayer(((Player) wrapped).getUniqueId()));
|
||||
} catch (ReflectiveOperationException ex1) {
|
||||
throw new RuntimeException("Failed to unwrap proxy " + wrapped, ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
unwrapperCache.put(type, unwrapper);
|
||||
UNWRAPPER_CACHE.put(type, unwrapper);
|
||||
return unwrapper;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
@@ -237,46 +247,42 @@ public class BukkitUnwrapper implements Unwrapper {
|
||||
|
||||
/**
|
||||
* Retrieve a cached unwrapper using the handle field.
|
||||
*
|
||||
* @param type - a cached field unwrapper.
|
||||
* @return The cached field unwrapper.
|
||||
*/
|
||||
private Unwrapper getFieldUnwrapper(final Class<?> type) {
|
||||
final Field find = FieldUtils.getField(type, "handle", true);
|
||||
|
||||
// See if we succeeded
|
||||
if (find != null) {
|
||||
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(type, "handle", null);
|
||||
if (accessor != null) {
|
||||
Unwrapper fieldUnwrapper = new Unwrapper() {
|
||||
@Override
|
||||
public Object unwrapItem(Object wrappedObject) {
|
||||
try {
|
||||
if (wrappedObject instanceof Class)
|
||||
return checkClass((Class<?>) wrappedObject, type, find.getType());
|
||||
return FieldUtils.readField(find, wrappedObject, true);
|
||||
} catch (IllegalAccessException e) {
|
||||
if (wrappedObject instanceof Class) {
|
||||
return checkClass((Class<?>) wrappedObject, type, accessor.getField().getType());
|
||||
}
|
||||
|
||||
return accessor.get(wrappedObject);
|
||||
} catch (IllegalStateException e) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_READ_FIELD_HANDLE).error(e).callerParam(wrappedObject, find)
|
||||
Report.newBuilder(REPORT_CANNOT_READ_FIELD_HANDLE).error(e)
|
||||
.callerParam(wrappedObject, accessor.getField())
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
unwrapperCache.put(type, fieldUnwrapper);
|
||||
|
||||
UNWRAPPER_CACHE.put(type, fieldUnwrapper);
|
||||
return fieldUnwrapper;
|
||||
|
||||
|
||||
} else {
|
||||
// Inform about this too
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_READ_FIELD_HANDLE).callerParam(find)
|
||||
Report.newBuilder(REPORT_CANNOT_READ_FIELD_HANDLE).callerParam(type)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> checkClass(Class<?> input, Class<?> expected, Class<?> result) {
|
||||
if (expected.isAssignableFrom(input)) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
/**
|
||||
* Represents a single delayed task.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class DelayedSingleTask {
|
||||
|
||||
protected int taskID = -1;
|
||||
protected Plugin plugin;
|
||||
protected BukkitScheduler scheduler;
|
||||
protected boolean closed;
|
||||
|
||||
/**
|
||||
* Create a single task scheduler.
|
||||
* @param plugin - owner plugin.
|
||||
*/
|
||||
public DelayedSingleTask(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.scheduler = plugin.getServer().getScheduler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single task scheduler.
|
||||
* @param plugin - owner plugin.
|
||||
* @param scheduler - specialized scheduler.
|
||||
*/
|
||||
public DelayedSingleTask(Plugin plugin, BukkitScheduler scheduler) {
|
||||
this.plugin = plugin;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a single task for execution.
|
||||
* <p>
|
||||
* Any previously scheduled task will be automatically cancelled.
|
||||
* <p>
|
||||
* Note that a tick delay of zero will execute the task immediately.
|
||||
*
|
||||
* @param ticksDelay - number of ticks before the task is executed.
|
||||
* @param task - the task to schedule.
|
||||
* @return TRUE if the task was successfully scheduled or executed, FALSE otherwise.
|
||||
*/
|
||||
public boolean schedule(long ticksDelay, Runnable task) {
|
||||
if (ticksDelay < 0)
|
||||
throw new IllegalArgumentException("Tick delay cannot be negative.");
|
||||
if (task == null)
|
||||
throw new IllegalArgumentException("task cannot be NULL");
|
||||
if (closed)
|
||||
return false;
|
||||
|
||||
// Special case
|
||||
if (ticksDelay == 0) {
|
||||
task.run();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Boilerplate, boilerplate
|
||||
final Runnable dispatch = task;
|
||||
|
||||
// Don't run multiple tasks!
|
||||
cancel();
|
||||
taskID = scheduler.scheduleSyncDelayedTask(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
dispatch.run();
|
||||
taskID = -1;
|
||||
}
|
||||
}, ticksDelay);
|
||||
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not a future task is scheduled to be executed.
|
||||
* @return TRUE if a current task has been scheduled for execution, FALSE otherwise.
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return taskID >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a future task from being executed.
|
||||
* @return TRUE if a task was cancelled, FALSE otherwise.
|
||||
*/
|
||||
public boolean cancel() {
|
||||
if (isRunning()) {
|
||||
scheduler.cancelTask(taskID);
|
||||
taskID = -1;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the raw task ID.
|
||||
* @return Raw task ID, or negative one if no task has been scheduled.
|
||||
*/
|
||||
public int getTaskID() {
|
||||
return taskID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the plugin this task belongs to.
|
||||
* @return The plugin scheduling the current taks.
|
||||
*/
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the current task and all future tasks scheduled by this instance.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
if (!closed) {
|
||||
cancel();
|
||||
plugin = null;
|
||||
scheduler = null;
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
close();
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,6 @@
|
||||
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
@@ -28,10 +24,20 @@ 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;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.World;
|
||||
@@ -44,51 +50,106 @@ import org.bukkit.entity.Player;
|
||||
* @author Kristian
|
||||
*/
|
||||
class EntityUtilities {
|
||||
private static final boolean NEW_TRACKER = MinecraftVersion.VILLAGE_UPDATE.atOrAbove();
|
||||
|
||||
private static final EntityUtilities INSTANCE = new EntityUtilities();
|
||||
private static final boolean NEW_TRACKER = MinecraftVersion.VILLAGE_UPDATE.atOrAbove();
|
||||
|
||||
private final Map<Class<?>, MethodAccessor> scanPlayersMethods = new ConcurrentHashMap<>();
|
||||
private final Map<Class<?>, FieldAccessor> trackedEntityFields = new ConcurrentHashMap<>();
|
||||
|
||||
private FieldAccessor chunkMapField;
|
||||
private FieldAccessor entityTrackerField;
|
||||
private FieldAccessor trackedPlayersField;
|
||||
private FieldAccessor trackedEntitiesField;
|
||||
|
||||
private MethodAccessor getEntity;
|
||||
private MethodAccessor getChunkProvider;
|
||||
|
||||
private EntityUtilities() {
|
||||
}
|
||||
|
||||
public static EntityUtilities getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private EntityUtilities() { }
|
||||
|
||||
private FieldAccessor entityTrackerField;
|
||||
private FieldAccessor trackedEntitiesField;
|
||||
private FieldAccessor trackedPlayersField;
|
||||
|
||||
private Map<Class<?>, MethodAccessor> scanPlayersMethods = new HashMap<>();
|
||||
|
||||
public void updateEntity(Entity entity, List<Player> observers) {
|
||||
if (entity == null || !entity.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<?> trackedPlayers = getTrackedPlayers(entity);
|
||||
List<Object> nmsPlayers = unwrapBukkit(observers);
|
||||
Collection<?> trackedPlayers = this.getTrackedPlayers(entity);
|
||||
List<Object> nmsPlayers = this.unwrapBukkit(observers);
|
||||
|
||||
trackedPlayers.removeAll(nmsPlayers);
|
||||
|
||||
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
|
||||
List<Object> removingEntries =
|
||||
MinecraftVersion.CAVES_CLIFFS_1.atOrAbove() ? this.getPlayerConnections(nmsPlayers) : nmsPlayers;
|
||||
trackedPlayers.removeAll(removingEntries);
|
||||
|
||||
// there can be multiple different entity tracker entry impls, see GH-732....
|
||||
scanPlayersMethods.computeIfAbsent(trackerEntry.getClass(), this::findScanPlayers).invoke(trackerEntry, nmsPlayers);
|
||||
Object trackerEntry = this.getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
|
||||
this.scanPlayersMethods.computeIfAbsent(trackerEntry.getClass(), this::findScanPlayers)
|
||||
.invoke(trackerEntry, nmsPlayers);
|
||||
}
|
||||
|
||||
public Entity getEntity(World world, int id) {
|
||||
// new tracker registers the entity before sending out any packets - we can use the supported getter method here
|
||||
if (NEW_TRACKER) {
|
||||
Object level = BukkitUnwrapper.getInstance().unwrapItem(world);
|
||||
if (this.getEntity == null) {
|
||||
Method entityGetter = FuzzyReflection.fromObject(level).getMethodByReturnTypeAndParameters(
|
||||
"getEntity",
|
||||
MinecraftReflection.getEntityClass(),
|
||||
int.class);
|
||||
this.getEntity = Accessors.getMethodAccessor(entityGetter);
|
||||
}
|
||||
|
||||
Object entity = this.getEntity.invoke(level, id);
|
||||
return (Entity) MinecraftReflection.getBukkitEntity(entity);
|
||||
}
|
||||
|
||||
// old tracker - now the pain begins
|
||||
Object trackerEntry = this.getEntityTrackerEntry(world, id);
|
||||
if (trackerEntry == null) {
|
||||
// not tracked
|
||||
return null;
|
||||
}
|
||||
|
||||
FieldAccessor trackedEntityAccessor = this.trackedEntityFields.computeIfAbsent(trackerEntry.getClass(), clz -> {
|
||||
try {
|
||||
// try to get the first entity field from the tracker class
|
||||
Field entityField = FuzzyReflection.fromClass(clz, true).getField(FuzzyFieldContract.newBuilder()
|
||||
.typeExact(MinecraftReflection.getEntityClass())
|
||||
.build());
|
||||
return Accessors.getFieldAccessor(entityField);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
// no such field, try the base tracker class instead
|
||||
Class<?> trackerClass = MinecraftReflection.getEntityTrackerClass();
|
||||
Field entityField = FuzzyReflection.fromClass(trackerClass, true).getField(FuzzyFieldContract.newBuilder()
|
||||
.typeExact(MinecraftReflection.getEntityClass())
|
||||
.build());
|
||||
return Accessors.getFieldAccessor(entityField);
|
||||
}
|
||||
});
|
||||
|
||||
Object entity = trackedEntityAccessor.get(trackerEntry);
|
||||
return (Entity) MinecraftReflection.getBukkitEntity(entity);
|
||||
}
|
||||
|
||||
private MethodAccessor findScanPlayers(Class<?> trackerClass) {
|
||||
MethodAccessor candidate = Accessors.getMethodAcccessorOrNull(trackerClass, "scanPlayers");
|
||||
MethodAccessor candidate = Accessors.getMethodAccessorOrNull(trackerClass, "scanPlayers");
|
||||
if (candidate != null) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(trackerClass, true);
|
||||
return Accessors.getMethodAccessor(
|
||||
fuzzy.getMethod(
|
||||
FuzzyMethodContract.newBuilder().returnTypeVoid().parameterExactArray(List.class).build()));
|
||||
return Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeVoid()
|
||||
.parameterExactArray(List.class)
|
||||
.build()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every client that is receiving information about a given entity.
|
||||
*
|
||||
* @param entity - the entity that is being tracked.
|
||||
* @return Every client/player that is tracking the given entity.
|
||||
* @throws FieldAccessException If reflection failed.
|
||||
@@ -99,11 +160,13 @@ class EntityUtilities {
|
||||
}
|
||||
|
||||
List<Player> result = new ArrayList<>();
|
||||
Collection<?> trackedPlayers = getTrackedPlayers(entity);
|
||||
Collection<?> trackedPlayers = this.getTrackedPlayers(entity);
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
@@ -114,68 +177,63 @@ class EntityUtilities {
|
||||
private Collection<?> getTrackedPlayers(Entity entity) {
|
||||
Validate.notNull(entity, "entity cannot be null");
|
||||
|
||||
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
|
||||
Object trackerEntry = this.getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
|
||||
Validate.notNull(trackerEntry, "Could not find entity trackers for " + entity);
|
||||
|
||||
if (trackedPlayersField == null) {
|
||||
trackedPlayersField = Accessors.getFieldAccessor(FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*"));
|
||||
if (this.trackedPlayersField == null) {
|
||||
this.trackedPlayersField = Accessors.getFieldAccessor(
|
||||
FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*"));
|
||||
}
|
||||
|
||||
Validate.notNull(trackedPlayersField, "Could not find trackedPlayers field");
|
||||
Validate.notNull(this.trackedPlayersField, "Could not find trackedPlayers field");
|
||||
|
||||
Object value = trackedPlayersField.get(trackerEntry);
|
||||
Object value = this.trackedPlayersField.get(trackerEntry);
|
||||
if (value instanceof Collection) {
|
||||
return (Collection<?>) value;
|
||||
} else if (value instanceof Map) {
|
||||
return ((Map<?, ?>) value).keySet();
|
||||
} else {
|
||||
// Please. No more changes.
|
||||
throw new IllegalStateException("trackedPlayers field was an unknown type: expected Collection or Map, but got " + value.getClass());
|
||||
throw new IllegalStateException(
|
||||
"trackedPlayers field was an unknown type: expected Collection or Map, but got " + value.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private MethodAccessor getChunkProvider;
|
||||
private FieldAccessor chunkMapField;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object getNewEntityTracker(Object worldServer, int entityId) {
|
||||
if (getChunkProvider == null) {
|
||||
if (this.getChunkProvider == null) {
|
||||
Class<?> chunkProviderClass = MinecraftReflection.getChunkProviderServer();
|
||||
getChunkProvider = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(worldServer.getClass(), false).getMethod(
|
||||
FuzzyMethodContract.newBuilder().parameterCount(0).returnTypeExact(chunkProviderClass).build()));
|
||||
this.getChunkProvider = Accessors.getMethodAccessor(FuzzyReflection.fromClass(worldServer.getClass(), false)
|
||||
.getMethod(FuzzyMethodContract.newBuilder().parameterCount(0).returnTypeExact(chunkProviderClass).build()));
|
||||
}
|
||||
|
||||
Object chunkProvider = getChunkProvider.invoke(worldServer);
|
||||
Object chunkProvider = this.getChunkProvider.invoke(worldServer);
|
||||
|
||||
if (chunkMapField == null) {
|
||||
if (this.chunkMapField == null) {
|
||||
Class<?> chunkMapClass = MinecraftReflection.getPlayerChunkMap();
|
||||
chunkMapField = Accessors.getFieldAccessor(
|
||||
FuzzyReflection.fromClass(chunkProvider.getClass(), false).getField(
|
||||
FuzzyFieldContract.newBuilder().typeExact(chunkMapClass).build()));
|
||||
this.chunkMapField = Accessors.getFieldAccessor(FuzzyReflection.fromClass(chunkProvider.getClass(), false)
|
||||
.getField(FuzzyFieldContract.newBuilder().typeExact(chunkMapClass).build()));
|
||||
}
|
||||
|
||||
Object playerChunkMap = chunkMapField.get(chunkProvider);
|
||||
Object playerChunkMap = this.chunkMapField.get(chunkProvider);
|
||||
|
||||
if (trackedEntitiesField == null) {
|
||||
if (this.trackedEntitiesField == null) {
|
||||
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()
|
||||
)
|
||||
);
|
||||
this.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()));
|
||||
this.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);
|
||||
Map<Integer, Object> trackedEntities = (Map<Integer, Object>) this.trackedEntitiesField.get(playerChunkMap);
|
||||
return trackedEntities.get(entityId);
|
||||
}
|
||||
|
||||
@@ -184,100 +242,47 @@ class EntityUtilities {
|
||||
Object worldServer = unwrapper.unwrapItem(world);
|
||||
|
||||
if (NEW_TRACKER) {
|
||||
return getNewEntityTracker(worldServer, entityID);
|
||||
return this.getNewEntityTracker(worldServer, entityID);
|
||||
}
|
||||
|
||||
if (entityTrackerField == null)
|
||||
entityTrackerField = Accessors.getFieldAccessor(FuzzyReflection.fromObject(worldServer).
|
||||
getFieldByType("tracker", MinecraftReflection.getEntityTrackerClass()));
|
||||
if (this.entityTrackerField == null) {
|
||||
this.entityTrackerField = Accessors.getFieldAccessor(FuzzyReflection.fromObject(worldServer).
|
||||
getFieldByType("tracker", MinecraftReflection.getEntityTrackerClass()));
|
||||
}
|
||||
|
||||
// Get the tracker
|
||||
Object tracker = entityTrackerField.get(worldServer);
|
||||
Object tracker = this.entityTrackerField.get(worldServer);
|
||||
|
||||
// Looking for an IntHashMap in the tracker entry
|
||||
if (trackedEntitiesField == null) {
|
||||
trackedEntitiesField = Accessors.getFieldAccessor(FuzzyReflection.fromObject(tracker, false)
|
||||
if (this.trackedEntitiesField == null) {
|
||||
this.trackedEntitiesField = Accessors.getFieldAccessor(FuzzyReflection.fromObject(tracker, false)
|
||||
.getFieldByType("trackedEntities", MinecraftReflection.getIntHashMapClass()));
|
||||
}
|
||||
|
||||
// Read the map
|
||||
Object trackedEntities = trackedEntitiesField.get(tracker);
|
||||
Object trackedEntities = this.trackedEntitiesField.get(tracker);
|
||||
return WrappedIntHashMap.fromHandle(trackedEntities).get(entityID);
|
||||
}
|
||||
|
||||
private Map<Class<?>, FieldAccessor> trackerFields = new ConcurrentHashMap<>();
|
||||
private MethodAccessor getEntityFromId;
|
||||
|
||||
/**
|
||||
* Retrieve entity from a ID, even it it's newly created.
|
||||
* @return The associated entity.
|
||||
* @throws FieldAccessException Reflection error.
|
||||
*/
|
||||
public Entity getEntityFromID(World world, int entityID) {
|
||||
Validate.notNull(world, "world cannot be null");
|
||||
Validate.isTrue(entityID >= 0, "entityID cannot be negative");
|
||||
|
||||
try {
|
||||
// first, try to read from the world
|
||||
// this should be good enough for most cases, but only exists in 1.14+
|
||||
if (NEW_TRACKER) {
|
||||
Object worldServer = BukkitUnwrapper.getInstance().unwrapItem(world);
|
||||
|
||||
if (getEntityFromId == null) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(worldServer.getClass(), false);
|
||||
getEntityFromId = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactArray(int.class)
|
||||
.returnTypeExact(MinecraftReflection.getEntityClass())
|
||||
.build()));
|
||||
}
|
||||
|
||||
Object entity = getEntityFromId.invoke(worldServer, entityID);
|
||||
if (entity != null) {
|
||||
return (Entity) MinecraftReflection.getBukkitEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// then go into the trackers
|
||||
Object trackerEntry = getEntityTrackerEntry(world, entityID);
|
||||
Object tracker = null;
|
||||
|
||||
if (trackerEntry != null) {
|
||||
// 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(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()));
|
||||
}
|
||||
});
|
||||
|
||||
tracker = trackerField.get(trackerEntry);
|
||||
}
|
||||
|
||||
// If the tracker is NULL, we'll just assume this entity doesn't exist
|
||||
return tracker != null ? (Entity) MinecraftReflection.getBukkitEntity(tracker) : null;
|
||||
} catch (Exception e) {
|
||||
throw new FieldAccessException("Cannot find entity from ID " + entityID + ".", e);
|
||||
}
|
||||
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();
|
||||
final List<Object> output = new ArrayList<>();
|
||||
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
|
||||
|
||||
// Get the NMS equivalent
|
||||
for (Player player : players) {
|
||||
Object result = unwrapper.unwrapItem(player);
|
||||
|
||||
if (result != null)
|
||||
if (result != null) {
|
||||
output.add(result);
|
||||
else
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot unwrap item " + player);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
* 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
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.comphenix.protocol.injector;
|
||||
|
||||
/**
|
||||
* The current player phase. This is used to limit the number of different injections.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public enum GamePhase {
|
||||
@@ -27,27 +27,29 @@ public enum GamePhase {
|
||||
* Only listen for packets sent or received before a player has logged in.
|
||||
*/
|
||||
LOGIN,
|
||||
|
||||
|
||||
/**
|
||||
* Only listen for packets sent or received after a player has logged in.
|
||||
*/
|
||||
PLAYING,
|
||||
|
||||
|
||||
/**
|
||||
* Listen for every sent and received packet.
|
||||
*/
|
||||
BOTH;
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the current value represents the login phase.
|
||||
*
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasLogin() {
|
||||
return this == LOGIN || this == BOTH;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the current value represents the playing phase.
|
||||
*
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasPlaying() {
|
||||
|
||||
@@ -1,49 +1,40 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
|
||||
/**
|
||||
* Yields access to the internal hook configuration.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface InternalManager extends ProtocolManager {
|
||||
/**
|
||||
* Retrieves how the server packets are read.
|
||||
* @return Injection method for reading server packets.
|
||||
*/
|
||||
public PlayerInjectHooks getPlayerHook();
|
||||
|
||||
/**
|
||||
* Sets how the server packets are read.
|
||||
* @param playerHook - the new injection method for reading server packets.
|
||||
*/
|
||||
public void setPlayerHook(PlayerInjectHooks playerHook);
|
||||
|
||||
/**
|
||||
* Register this protocol manager on Bukkit.
|
||||
*
|
||||
* @param manager - Bukkit plugin manager that provides player join/leave events.
|
||||
* @param plugin - the parent plugin.
|
||||
* @param plugin - the parent plugin.
|
||||
*/
|
||||
public void registerEvents(PluginManager manager, final Plugin plugin);
|
||||
|
||||
void registerEvents(PluginManager manager, final Plugin plugin);
|
||||
|
||||
/**
|
||||
* Called when ProtocolLib is closing.
|
||||
*/
|
||||
public void close();
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Determine if debug mode is enabled.
|
||||
*
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean isDebug();
|
||||
|
||||
boolean isDebug();
|
||||
|
||||
/**
|
||||
* Set whether or not debug mode is enabled.
|
||||
*
|
||||
* @param debug - TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public void setDebug(boolean debug);
|
||||
void setDebug(boolean debug);
|
||||
}
|
||||
@@ -2,16 +2,16 @@
|
||||
* 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
|
||||
*/
|
||||
|
||||
@@ -22,25 +22,28 @@ import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
/**
|
||||
* Represents an object that initiate the packet listeners.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface ListenerInvoker {
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener.
|
||||
*
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
void invokePacketRecieving(PacketEvent event);
|
||||
void invokePacketReceiving(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener.
|
||||
*
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
void invokePacketSending(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Retrieve the associated type of a packet.
|
||||
*
|
||||
* @param packet - the packet.
|
||||
* @return The packet type.
|
||||
*/
|
||||
|
||||
@@ -1,108 +1,74 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketOutputHandler;
|
||||
import com.comphenix.protocol.events.PacketPostListener;
|
||||
import com.comphenix.protocol.events.ScheduledPacket;
|
||||
import java.util.Deque;
|
||||
|
||||
/**
|
||||
* Represents a processor for network markers.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class NetworkProcessor {
|
||||
private ErrorReporter reporter;
|
||||
|
||||
|
||||
private final ErrorReporter reporter;
|
||||
|
||||
/**
|
||||
* Construct a new network processor.
|
||||
*
|
||||
* @param reporter - the reporter.
|
||||
*/
|
||||
public NetworkProcessor(ErrorReporter reporter) {
|
||||
this.reporter = reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the serialized packet byte array with the given network marker.
|
||||
* @param event - current packet event.
|
||||
* @param marker - the network marker.
|
||||
* @param input - the input array.
|
||||
* @return The output array.
|
||||
*/
|
||||
public byte[] processOutput(PacketEvent event, NetworkMarker marker, final byte[] input) {
|
||||
// Bit of a hack - but we need the performance
|
||||
PriorityQueue<PacketOutputHandler> handlers = (PriorityQueue<PacketOutputHandler>)
|
||||
marker.getOutputHandlers();
|
||||
byte[] output = input;
|
||||
|
||||
// Let each handler prepare the actual output
|
||||
while (!handlers.isEmpty()) {
|
||||
PacketOutputHandler handler = handlers.poll();
|
||||
|
||||
try {
|
||||
byte[] changed = handler.handle(event, output);
|
||||
|
||||
// Don't break just because a plugin returned NULL
|
||||
if (changed != null) {
|
||||
output = changed;
|
||||
} else {
|
||||
throw new IllegalStateException("Handler cannot return a NULL array.");
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
reporter.reportMinimal(handler.getPlugin(), "PacketOutputHandler.handle()", e);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the post listeners and packet transmission, if any.
|
||||
* @param event - PacketEvent
|
||||
*
|
||||
* @param event - PacketEvent
|
||||
* @param marker - the network marker, or NULL.
|
||||
*/
|
||||
public void invokePostEvent(PacketEvent event, NetworkMarker marker) {
|
||||
if (marker == null)
|
||||
if (marker == null) {
|
||||
return;
|
||||
|
||||
if (NetworkMarker.hasPostListeners(marker)) {
|
||||
}
|
||||
|
||||
if (event != null && NetworkMarker.hasPostListeners(marker)) {
|
||||
// Invoke every sent listener
|
||||
for (PacketPostListener listener : marker.getPostListeners()) {
|
||||
try {
|
||||
listener.onPostEvent(event);
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
} catch (OutOfMemoryError | ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
reporter.reportMinimal(listener.getPlugin(), "SentListener.run()", e);
|
||||
this.reporter.reportMinimal(listener.getPlugin(), "SentListener.run()", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
sendScheduledPackets(marker);
|
||||
|
||||
this.sendScheduledPackets(marker);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send any scheduled packets.
|
||||
*
|
||||
* @param marker - the network marker.
|
||||
*/
|
||||
private void sendScheduledPackets(NetworkMarker marker) {
|
||||
// Next, invoke post packet transmission
|
||||
List<ScheduledPacket> scheduled = NetworkMarker.readScheduledPackets(marker);
|
||||
Deque<ScheduledPacket> scheduled = NetworkMarker.readScheduledPackets(marker);
|
||||
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
|
||||
if (scheduled != null) {
|
||||
for (ScheduledPacket packet : scheduled) {
|
||||
ScheduledPacket packet;
|
||||
while ((packet = scheduled.poll()) != null) {
|
||||
packet.schedule(manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,6 @@
|
||||
|
||||
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;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
@@ -31,70 +26,117 @@ import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A packet constructor that uses an internal Minecraft.
|
||||
* @author Kristian
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PacketConstructor {
|
||||
|
||||
/**
|
||||
* A packet constructor that automatically converts Bukkit types to their NMS conterpart.
|
||||
* <p>
|
||||
* Remember to call withPacket().
|
||||
*/
|
||||
public static PacketConstructor DEFAULT = new PacketConstructor(null);
|
||||
|
||||
|
||||
// The constructor method that's actually responsible for creating the packet
|
||||
private Constructor<?> constructorMethod;
|
||||
|
||||
private final Constructor<?> constructorMethod;
|
||||
// Used to unwrap Bukkit objects
|
||||
private final List<Unwrapper> unwrappers;
|
||||
|
||||
// The packet ID
|
||||
private PacketType type;
|
||||
|
||||
// Used to unwrap Bukkit objects
|
||||
private List<Unwrapper> unwrappers;
|
||||
|
||||
// Parameters that need to be unwrapped
|
||||
private Unwrapper[] paramUnwrapper;
|
||||
|
||||
|
||||
private PacketConstructor(Constructor<?> constructorMethod) {
|
||||
this.constructorMethod = constructorMethod;
|
||||
this.unwrappers = Lists.newArrayList((Unwrapper) new BukkitUnwrapper(new RethrowErrorReporter() ));
|
||||
this.unwrappers = Lists.newArrayList((Unwrapper) new BukkitUnwrapper(new RethrowErrorReporter()));
|
||||
this.unwrappers.addAll(BukkitConverters.getUnwrappers());
|
||||
}
|
||||
|
||||
private PacketConstructor(PacketType type, Constructor<?> constructorMethod, List<Unwrapper> unwrappers, Unwrapper[] paramUnwrapper) {
|
||||
|
||||
private PacketConstructor(PacketType type, Constructor<?> constructorMethod, List<Unwrapper> unwrappers,
|
||||
Unwrapper[] paramUnwrapper) {
|
||||
this.type = type;
|
||||
this.constructorMethod = constructorMethod;
|
||||
this.unwrappers = unwrappers;
|
||||
this.paramUnwrapper = paramUnwrapper;
|
||||
}
|
||||
|
||||
|
||||
// Determine if a method with the types 'params' can be called with 'types'
|
||||
private static boolean isCompatible(Class<?>[] types, Class<?>[] params) {
|
||||
|
||||
// Determine if the types are similar
|
||||
if (params.length == types.length) {
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
Class<?> inputType = types[i];
|
||||
Class<?> paramType = params[i];
|
||||
|
||||
// The input type is always wrapped
|
||||
if (!inputType.isPrimitive() && paramType.isPrimitive()) {
|
||||
// Wrap it
|
||||
paramType = Primitives.wrap(paramType);
|
||||
}
|
||||
|
||||
// Compare assignability
|
||||
if (!paramType.isAssignableFrom(inputType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parameter count must match
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the class of an object, or just the class if it already is a class object.
|
||||
*
|
||||
* @param obj - the object.
|
||||
* @return The class of an object.
|
||||
*/
|
||||
public static Class<?> getClass(Object obj) {
|
||||
if (obj instanceof Class) {
|
||||
return (Class<?>) obj;
|
||||
}
|
||||
return obj.getClass();
|
||||
}
|
||||
|
||||
public ImmutableList<Unwrapper> getUnwrappers() {
|
||||
return ImmutableList.copyOf(unwrappers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the id of the packets this constructor creates.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getType()} instead.
|
||||
*
|
||||
* @return The ID of the packets this constructor will create.
|
||||
*/
|
||||
@Deprecated
|
||||
public int getPacketID() {
|
||||
return type.getCurrentId();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the type of the packets this constructor creates.
|
||||
*
|
||||
* @return The type of the created packets.
|
||||
*/
|
||||
public PacketType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a copy of the current constructor with a different list of unwrappers.
|
||||
*
|
||||
* @param unwrappers - list of unwrappers that convert Bukkit wrappers into the equivalent NMS classes.
|
||||
* @return A constructor with a different set of unwrappers.
|
||||
*/
|
||||
@@ -106,7 +148,8 @@ public class PacketConstructor {
|
||||
* Create a packet constructor that creates packets using the given types.
|
||||
* <p>
|
||||
* Note that if you pass a Class as a value, it will use its type directly.
|
||||
* @param type - the type of the packet to create.
|
||||
*
|
||||
* @param type - the type of the packet to create.
|
||||
* @param values - the values that will match each parameter in the desired constructor.
|
||||
* @return A packet constructor with these types.
|
||||
* @throws IllegalArgumentException If no packet constructor could be created with these types.
|
||||
@@ -115,15 +158,15 @@ public class PacketConstructor {
|
||||
Class<?>[] types = new Class<?>[values.length];
|
||||
Throwable lastException = null;
|
||||
Unwrapper[] paramUnwrapper = new Unwrapper[values.length];
|
||||
|
||||
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
// Default type
|
||||
if (values[i] != null) {
|
||||
types[i] = PacketConstructor.getClass(values[i]);
|
||||
|
||||
|
||||
for (Unwrapper unwrapper : unwrappers) {
|
||||
Object result = null;
|
||||
|
||||
|
||||
try {
|
||||
result = unwrapper.unwrapItem(values[i]);
|
||||
} catch (OutOfMemoryError e) {
|
||||
@@ -133,7 +176,7 @@ public class PacketConstructor {
|
||||
} catch (Throwable e) {
|
||||
lastException = e;
|
||||
}
|
||||
|
||||
|
||||
// Update type we're searching for
|
||||
if (result != null) {
|
||||
types[i] = PacketConstructor.getClass(result);
|
||||
@@ -141,7 +184,7 @@ public class PacketConstructor {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// Try it
|
||||
types[i] = Object.class;
|
||||
@@ -161,14 +204,15 @@ public class PacketConstructor {
|
||||
}
|
||||
throw new IllegalArgumentException("No suitable constructor could be found.", lastException);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a packet using the special builtin Minecraft constructors.
|
||||
*
|
||||
* @param values - values containing Bukkit wrapped items to pass to Minecraft.
|
||||
* @return The created packet.
|
||||
* @throws FieldAccessException Failure due to a security limitation.
|
||||
* @throws FieldAccessException Failure due to a security limitation.
|
||||
* @throws IllegalArgumentException Arguments doesn't match the constructor.
|
||||
* @throws RuntimeException Minecraft threw an exception.
|
||||
* @throws RuntimeException Minecraft threw an exception.
|
||||
*/
|
||||
public PacketContainer createPacket(Object... values) throws FieldAccessException {
|
||||
try {
|
||||
@@ -178,10 +222,10 @@ public class PacketConstructor {
|
||||
values[i] = paramUnwrapper[i].unwrapItem(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Object nmsPacket = constructorMethod.newInstance(values);
|
||||
return new PacketContainer(type, nmsPacket);
|
||||
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (InstantiationException e) {
|
||||
@@ -192,57 +236,20 @@ public class PacketConstructor {
|
||||
throw new RuntimeException("Minecraft error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if a method with the types 'params' can be called with 'types'
|
||||
private static boolean isCompatible(Class<?>[] types, Class<?>[] params) {
|
||||
|
||||
// Determine if the types are similar
|
||||
if (params.length == types.length) {
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
Class<?> inputType = types[i];
|
||||
Class<?> paramType = params[i];
|
||||
|
||||
// The input type is always wrapped
|
||||
if (!inputType.isPrimitive() && paramType.isPrimitive()) {
|
||||
// Wrap it
|
||||
paramType = Primitives.wrap(paramType);
|
||||
}
|
||||
|
||||
// Compare assignability
|
||||
if (!paramType.isAssignableFrom(inputType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parameter count must match
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the class of an object, or just the class if it already is a class object.
|
||||
* @param obj - the object.
|
||||
* @return The class of an object.
|
||||
*/
|
||||
public static Class<?> getClass(Object obj) {
|
||||
if (obj instanceof Class)
|
||||
return (Class<?>) obj;
|
||||
return obj.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a unwrapper for a constructor parameter.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public static interface Unwrapper {
|
||||
|
||||
/**
|
||||
* Convert the given wrapped object to the equivalent net.minecraft.server object.
|
||||
* <p>
|
||||
* Note that we may pass in a class instead of object - in that case, the unwrapper should
|
||||
* return the equivalent NMS class.
|
||||
* Note that we may pass in a class instead of object - in that case, the unwrapper should return the equivalent NMS
|
||||
* class.
|
||||
*
|
||||
* @param wrappedObject - wrapped object or class.
|
||||
* @return The equivalent net.minecraft.server object or class.
|
||||
*/
|
||||
|
||||
@@ -1,191 +1,122 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class PacketFilterBuilder {
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private Server server;
|
||||
private Plugin library;
|
||||
private MinecraftVersion mcVersion;
|
||||
private DelayedSingleTask unhookTask;
|
||||
private ErrorReporter reporter;
|
||||
|
||||
// Whether or not we need to enable Netty
|
||||
private AsyncFilterManager asyncManager;
|
||||
private boolean nettyEnabled;
|
||||
|
||||
/**
|
||||
* Update the current class loader.
|
||||
* @param classLoader - current class loader.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder classLoader(@Nonnull ClassLoader classLoader) {
|
||||
if (classLoader == null)
|
||||
throw new IllegalArgumentException("classLoader cannot be NULL.");
|
||||
this.classLoader = classLoader;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current server.
|
||||
*
|
||||
* @param server - current server.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder server(@Nonnull Server server) {
|
||||
if (server == null)
|
||||
throw new IllegalArgumentException("server cannot be NULL.");
|
||||
this.server = server;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a reference to the plugin instance of ProtocolLib.
|
||||
*
|
||||
* @param library - plugin instance.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder library(@Nonnull Plugin library) {
|
||||
if (library == null)
|
||||
throw new IllegalArgumentException("library cannot be NULL.");
|
||||
this.library = library;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the current Minecraft version.
|
||||
*
|
||||
* @param mcVersion - Minecraft version.
|
||||
* @return This builder, for chaining.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder minecraftVersion(@Nonnull MinecraftVersion mcVersion) {
|
||||
if (mcVersion == null)
|
||||
throw new IllegalArgumentException("minecraftVersion cannot be NULL.");
|
||||
this.mcVersion = mcVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the task used to delay unhooking when ProtocolLib is no in use.
|
||||
* @param unhookTask - the unhook task.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder unhookTask(@Nonnull DelayedSingleTask unhookTask) {
|
||||
if (unhookTask == null)
|
||||
throw new IllegalArgumentException("unhookTask cannot be NULL.");
|
||||
this.unhookTask = unhookTask;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the error reporter.
|
||||
*
|
||||
* @param reporter - new error reporter.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder reporter(@Nonnull ErrorReporter reporter) {
|
||||
if (reporter == null)
|
||||
throw new IllegalArgumentException("reporter cannot be NULL.");
|
||||
this.reporter = reporter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we should prepare to hook Netty in Spigot.
|
||||
* <p>
|
||||
* This is calculated in the {@link #build()} method.
|
||||
* @return TRUE if we should, FALSE otherwise.
|
||||
*/
|
||||
public boolean isNettyEnabled() {
|
||||
return nettyEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the class loader set in this builder.
|
||||
* @return The class loader.
|
||||
*/
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current CraftBukkit server.
|
||||
*
|
||||
* @return Current server.
|
||||
*/
|
||||
public Server getServer() {
|
||||
return server;
|
||||
return this.server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a reference to the current ProtocolLib instance.
|
||||
*
|
||||
* @return ProtocolLib.
|
||||
*/
|
||||
public Plugin getLibrary() {
|
||||
return library;
|
||||
return this.library;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current Minecraft version.
|
||||
*
|
||||
* @return Current version.
|
||||
*/
|
||||
public MinecraftVersion getMinecraftVersion() {
|
||||
return mcVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the task that is used to delay unhooking when ProtocolLib is no in use.
|
||||
* @return The unhook task.
|
||||
*/
|
||||
public DelayedSingleTask getUnhookTask() {
|
||||
return unhookTask;
|
||||
return this.mcVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the error reporter.
|
||||
*
|
||||
* @return Error reporter.
|
||||
*/
|
||||
public ErrorReporter getReporter() {
|
||||
return reporter;
|
||||
return this.reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the asynchronous manager.
|
||||
* <p>
|
||||
* This is first constructed the {@link #build()} method.
|
||||
*
|
||||
* @return The asynchronous manager.
|
||||
*/
|
||||
public AsyncFilterManager getAsyncManager() {
|
||||
return asyncManager;
|
||||
return this.asyncManager;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new packet filter manager.
|
||||
*
|
||||
* @return A new packet filter manager.
|
||||
*/
|
||||
public InternalManager build() {
|
||||
if (reporter == null)
|
||||
if (this.reporter == null) {
|
||||
throw new IllegalArgumentException("reporter cannot be NULL.");
|
||||
if (classLoader == null)
|
||||
throw new IllegalArgumentException("classLoader cannot be NULL.");
|
||||
|
||||
asyncManager = new AsyncFilterManager(reporter, server.getScheduler());
|
||||
nettyEnabled = false;
|
||||
}
|
||||
|
||||
return buildInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new packet filter manager without checking for Netty.
|
||||
* @return A new packet filter manager.
|
||||
*/
|
||||
private PacketFilterManager buildInternal() {
|
||||
PacketFilterManager manager = new PacketFilterManager(this);
|
||||
|
||||
// It's a cyclic reference, but it's too late to fix now
|
||||
asyncManager.setManager(manager);
|
||||
return manager;
|
||||
this.asyncManager = new AsyncFilterManager(this.reporter, this.server.getScheduler());
|
||||
return new PacketFilterManager(this);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,31 +0,0 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
/**
|
||||
* Sets the inject hook type. Different types allow for maximum compatibility.
|
||||
* @author Kristian
|
||||
*/
|
||||
public enum PlayerInjectHooks {
|
||||
/**
|
||||
* The injection hook that does nothing. Set when every other inject hook fails.
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Override the network handler object itself. Only works in 1.3.
|
||||
* <p>
|
||||
* Cannot intercept MapChunk packets.
|
||||
*/
|
||||
NETWORK_MANAGER_OBJECT,
|
||||
|
||||
/**
|
||||
* Override the packet queue lists in NetworkHandler.
|
||||
* <p>
|
||||
* Cannot intercept MapChunk packets.
|
||||
*/
|
||||
NETWORK_HANDLER_FIELDS,
|
||||
|
||||
/**
|
||||
* Override the server handler object. Versatile, but a tad slower.
|
||||
*/
|
||||
NETWORK_SERVER_OBJECT;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
/**
|
||||
* Invoked when attempting to use a player that has already logged out.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PlayerLoggedOutException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Generated by Eclipse.
|
||||
*/
|
||||
private static final long serialVersionUID = 4889257862160145234L;
|
||||
|
||||
public PlayerLoggedOutException() {
|
||||
// Default error message
|
||||
super("Cannot inject a player that has already logged out.");
|
||||
}
|
||||
|
||||
public PlayerLoggedOutException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PlayerLoggedOutException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PlayerLoggedOutException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an exception from a formatted message.
|
||||
* @param message - the message to format.
|
||||
* @param params - parameters.
|
||||
* @return The formated exception
|
||||
*/
|
||||
public static PlayerLoggedOutException fromFormat(String message, Object... params) {
|
||||
return new PlayerLoggedOutException(String.format(message, params));
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginLoadOrder;
|
||||
|
||||
@@ -55,12 +56,12 @@ class PluginVerifier {
|
||||
/**
|
||||
* Contains a list of plugins that will detect and use ProtocolLib dynamically, instead of relying on the dependency system.
|
||||
*/
|
||||
private static final Set<String> DYNAMIC_DEPENDENCY = Sets.newHashSet("mcore", "MassiveCore");
|
||||
private static final Set<String> DYNAMIC_DEPENDENCY = ImmutableSet.of("mcore", "MassiveCore");
|
||||
|
||||
/**
|
||||
* Set of plugins that have been loaded after ProtocolLib.
|
||||
*/
|
||||
private final Set<String> loadedAfter = new HashSet<String>();
|
||||
private final Set<String> loadedAfter = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Reference to ProtocolLib.
|
||||
@@ -201,7 +202,7 @@ class PluginVerifier {
|
||||
if (list == null)
|
||||
return Collections.emptySet();
|
||||
else
|
||||
return Sets.newHashSet(list);
|
||||
return new HashSet<>(list);
|
||||
}
|
||||
|
||||
// Avoid cycles. DFS.
|
||||
|
||||
@@ -50,7 +50,7 @@ public class PrioritizedListener<TListener> implements Comparable<PrioritizedLis
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// We only care about the listener - priority itself should not make a difference
|
||||
if(obj instanceof PrioritizedListener){
|
||||
if (obj instanceof PrioritizedListener) {
|
||||
final PrioritizedListener<TListener> other = (PrioritizedListener<TListener>) obj;
|
||||
return Objects.equal(listener, other.listener);
|
||||
} else {
|
||||
|
||||
@@ -17,77 +17,93 @@
|
||||
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
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.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.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.ZeroBuffer;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy.Default;
|
||||
import net.bytebuddy.implementation.FixedValue;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
|
||||
/**
|
||||
* Caches structure modifiers.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class StructureCache {
|
||||
|
||||
// Structure modifiers
|
||||
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<>();
|
||||
private static final Map<Class<?>, Supplier<Object>> PACKET_INSTANCE_CREATORS = new ConcurrentHashMap<>();
|
||||
private static final Map<PacketType, StructureModifier<Object>> STRUCTURE_MODIFIER_CACHE = 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 boolean TRICK_TRIED = false;
|
||||
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_BASE;
|
||||
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_JSON;
|
||||
|
||||
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 () -> {
|
||||
public static Object newPacket(Class<?> packetClass) {
|
||||
Supplier<Object> packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, clazz -> {
|
||||
// prefer construction via PacketDataSerializer constructor on 1.17 and above
|
||||
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
||||
ConstructorAccessor serializerAccessor = Accessors.getConstructorAccessorOrNull(
|
||||
clazz,
|
||||
MinecraftReflection.getPacketDataSerializerClass());
|
||||
if (serializerAccessor != null) {
|
||||
// check if the method is possible
|
||||
if (tryInitTrickDataSerializer()) {
|
||||
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();
|
||||
}
|
||||
// first try with the base accessor
|
||||
Object serializer = TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer());
|
||||
serializerAccessor.invoke(serializer); // throwaway instance, for testing
|
||||
|
||||
return result;
|
||||
// method is working
|
||||
return () -> serializerAccessor.invoke(serializer);
|
||||
} catch (Exception exception) {
|
||||
try {
|
||||
// try with the json accessor
|
||||
Object serializer = TRICKED_DATA_SERIALIZER_JSON.invoke(new ZeroBuffer());
|
||||
serializerAccessor.invoke(serializer); // throwaway instance, for testing
|
||||
|
||||
// method is working
|
||||
return () -> serializerAccessor.invoke(serializer);
|
||||
} catch (Exception ignored) {
|
||||
// shrug, fall back to default behaviour
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try via DefaultInstances as fallback
|
||||
return () -> {
|
||||
Object packetInstance = DefaultInstances.DEFAULT.create(clazz);
|
||||
Objects.requireNonNull(packetInstance, "Unable to create packet instance for class " + clazz);
|
||||
return packetInstance;
|
||||
};
|
||||
});
|
||||
return packetConstructor.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty Minecraft packet of the given type.
|
||||
*
|
||||
* @param type - packet type.
|
||||
* @return Created packet.
|
||||
*/
|
||||
@@ -95,106 +111,92 @@ public class StructureCache {
|
||||
return newPacket(PacketRegistry.getPacketClassFromType(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier for the given packet type.
|
||||
* @param type - packet type.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
public static StructureModifier<Object> getStructure(PacketType type) {
|
||||
// Compile structures by default
|
||||
return getStructure(type, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier given a packet type.
|
||||
*
|
||||
* @param packetType - packet type.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
public static StructureModifier<Object> getStructure(Class<?> packetType) {
|
||||
// Compile structures by default
|
||||
return getStructure(packetType, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier given a packet type.
|
||||
* @param packetType - packet type.
|
||||
* @param compile - whether or not to asynchronously compile the structure modifier.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
public static StructureModifier<Object> getStructure(Class<?> packetType, boolean compile) {
|
||||
// Get the ID from the class
|
||||
PacketType type = PacketRegistry.getPacketType(packetType);
|
||||
Preconditions.checkNotNull(type, "No packet type associated with " + packetType);
|
||||
return getStructure(type, compile);
|
||||
return getStructure(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier for the given packet type.
|
||||
* @param type - packet type.
|
||||
* @param compile - whether or not to asynchronously compile the structure modifier.
|
||||
*
|
||||
* @param packetType - packet type.
|
||||
* @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);
|
||||
public static StructureModifier<Object> getStructure(final PacketType packetType) {
|
||||
Preconditions.checkNotNull(packetType, "type cannot be null");
|
||||
|
||||
// We don't want to create this for every lookup
|
||||
if (result == null) {
|
||||
// Use the vanilla class definition
|
||||
final StructureModifier<Object> value = new StructureModifier<>(
|
||||
PacketRegistry.getPacketClassFromType(type), MinecraftReflection.getPacketClass(), true);
|
||||
|
||||
result = structureModifiers.putIfAbsent(type, value);
|
||||
|
||||
// We may end up creating multiple modifiers, but we'll agree on which to use
|
||||
if (result == null) {
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically compile the structure modifier
|
||||
if (compile && !(result instanceof CompiledStructureModifier)) {
|
||||
// Compilation is many orders of magnitude slower than synchronization
|
||||
synchronized (compiling) {
|
||||
final BackgroundCompiler compiler = BackgroundCompiler.getInstance();
|
||||
|
||||
if (!compiling.contains(type) && compiler != null) {
|
||||
compiler.scheduleCompilation(result,
|
||||
compiledModifier -> structureModifiers.put(type, compiledModifier));
|
||||
compiling.add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return STRUCTURE_MODIFIER_CACHE.computeIfAbsent(packetType, type -> {
|
||||
Class<?> packetClass = PacketRegistry.getPacketClassFromType(type);
|
||||
return new StructureModifier<>(packetClass, MinecraftReflection.getPacketClass(), true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Returns a new mocked null data serializer instance, if possible.
|
||||
*
|
||||
* @return a new null data serializer instance.
|
||||
*/
|
||||
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
|
||||
}
|
||||
public static Object newNullDataSerializer() {
|
||||
tryInitTrickDataSerializer();
|
||||
return TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static boolean tryInitTrickDataSerializer() {
|
||||
if (TRICK_TRIED) {
|
||||
return TRICKED_DATA_SERIALIZER_BASE != null;
|
||||
}
|
||||
return TRICKED_DATA_SERIALIZER;
|
||||
|
||||
// prevent double init
|
||||
TRICK_TRIED = true;
|
||||
|
||||
try {
|
||||
// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
|
||||
Object textCompound = WrappedChatComponent.fromText("").getHandle();
|
||||
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
|
||||
// base builder which intercepts a few methods
|
||||
DynamicType.Builder<?> baseBuilder = ByteBuddyFactory.getInstance()
|
||||
.createSubclass(MinecraftReflection.getPacketDataSerializerClass())
|
||||
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
|
||||
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
|
||||
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
|
||||
.intercept(FixedValue.value(compound))
|
||||
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
|
||||
.intercept(FixedValue.value(textCompound))
|
||||
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
|
||||
.intercept(FixedValue.nullValue());
|
||||
Class<?> serializerBase = baseBuilder.make()
|
||||
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
|
||||
.getLoaded();
|
||||
TRICKED_DATA_SERIALIZER_BASE = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
|
||||
|
||||
// extended builder which intercepts the read string method as well
|
||||
Class<?> withStringIntercept = baseBuilder
|
||||
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson")
|
||||
.method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class)))
|
||||
.intercept(FixedValue.value("{}"))
|
||||
.make()
|
||||
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
|
||||
.getLoaded();
|
||||
TRICKED_DATA_SERIALIZER_JSON = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);
|
||||
|
||||
// worked
|
||||
return true;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// didn't work
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||
|
||||
public abstract class AbstractPacketInjector implements PacketInjector {
|
||||
private PacketTypeSet reveivedFilters;
|
||||
|
||||
public AbstractPacketInjector(PacketTypeSet reveivedFilters) {
|
||||
this.reveivedFilters = reveivedFilters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled(Object packet) {
|
||||
// No, it's never cancelled
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(Object packet, boolean cancelled) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
||||
reveivedFilters.addType(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removePacketHandler(PacketType type) {
|
||||
reveivedFilters.removeType(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPacketHandler(PacketType type) {
|
||||
return reveivedFilters.contains(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getPacketHandlers() {
|
||||
return reveivedFilters.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupAll() {
|
||||
reveivedFilters.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
|
||||
public abstract class AbstractPlayerHandler implements PlayerInjectionHandler {
|
||||
protected PacketTypeSet sendingFilters;
|
||||
|
||||
public AbstractPlayerHandler(PacketTypeSet sendingFilters) {
|
||||
this.sendingFilters = sendingFilters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerHook(GamePhase phase, PlayerInjectHooks playerHook) {
|
||||
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
||||
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
||||
sendingFilters.addType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePacketHandler(PacketType type) {
|
||||
sendingFilters.removeType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getSendingFilters() {
|
||||
return sendingFilters.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
sendingFilters.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerInjectHooks getPlayerHook(GamePhase phase) {
|
||||
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRecievePackets() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerInjectHooks getPlayerHook() {
|
||||
// Pretend that we do
|
||||
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayerByConnection(DataInputStream inputStream) throws InterruptedException {
|
||||
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkListener(PacketListener listener) {
|
||||
// They're all fine!
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkListener(Set<PacketListener> listeners) {
|
||||
// Yes, really
|
||||
}
|
||||
}
|
||||
@@ -1,234 +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 io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class BootstrapList implements List<Object> {
|
||||
private List<Object> delegate;
|
||||
private ChannelHandler handler;
|
||||
|
||||
/**
|
||||
* Construct a new bootstrap list.
|
||||
* @param delegate - the delegate.
|
||||
* @param handler - the channel handler to add.
|
||||
*/
|
||||
public BootstrapList(List<Object> delegate, ChannelHandler handler) {
|
||||
this.delegate = delegate;
|
||||
this.handler = handler;
|
||||
|
||||
// Process all existing bootstraps
|
||||
for (Object item : this) {
|
||||
processElement(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean add(Object element) {
|
||||
processElement(element);
|
||||
return delegate.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addAll(Collection<? extends Object> collection) {
|
||||
List<Object> copy = Lists.newArrayList(collection);
|
||||
|
||||
// Process the collection before we pass it on
|
||||
for (Object element : copy) {
|
||||
processElement(element);
|
||||
}
|
||||
return delegate.addAll(copy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object set(int index, Object element) {
|
||||
Object old = delegate.set(index, element);
|
||||
|
||||
// Handle the old future, and the newly inserted future
|
||||
if (old != element) {
|
||||
unprocessElement(old);
|
||||
processElement(element);
|
||||
}
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single element.
|
||||
* @param element - the element.
|
||||
*/
|
||||
protected void processElement(Object element) {
|
||||
if (element instanceof ChannelFuture) {
|
||||
processBootstrap((ChannelFuture) element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unprocess a single element.
|
||||
* @param element - the element to unprocess.
|
||||
*/
|
||||
protected void unprocessElement(Object element) {
|
||||
if (element instanceof ChannelFuture) {
|
||||
unprocessBootstrap((ChannelFuture) element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single channel future.
|
||||
* @param future - the future.
|
||||
*/
|
||||
protected void processBootstrap(ChannelFuture future) {
|
||||
// Important: Must be addFirst()
|
||||
future.channel().pipeline().addFirst(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert any changes we made to the channel future.
|
||||
* @param future - the future.
|
||||
*/
|
||||
protected void unprocessBootstrap(ChannelFuture future) {
|
||||
final Channel channel = future.channel();
|
||||
|
||||
// For thread safety - see ChannelInjector.close()
|
||||
channel.eventLoop().submit(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
channel.pipeline().remove(handler);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close and revert all changes.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
for (Object element : this)
|
||||
unprocessElement(element);
|
||||
}
|
||||
|
||||
// Boiler plate
|
||||
@Override
|
||||
public synchronized int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isEmpty() {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return delegate.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Iterator<Object> iterator() {
|
||||
return delegate.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object[] toArray() {
|
||||
return delegate.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized <T> T[] toArray(T[] a) {
|
||||
return delegate.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean remove(Object o) {
|
||||
return delegate.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean containsAll(Collection<?> c) {
|
||||
return delegate.containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addAll(int index, Collection<? extends Object> c) {
|
||||
return delegate.addAll(index, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean removeAll(Collection<?> c) {
|
||||
return delegate.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean retainAll(Collection<?> c) {
|
||||
return delegate.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clear() {
|
||||
delegate.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object get(int index) {
|
||||
return delegate.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void add(int index, Object element) {
|
||||
delegate.add(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object remove(int index) {
|
||||
return delegate.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int indexOf(Object o) {
|
||||
return delegate.indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int lastIndexOf(Object o) {
|
||||
return delegate.lastIndexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ListIterator<Object> listIterator() {
|
||||
return delegate.listIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ListIterator<Object> listIterator(int index) {
|
||||
return delegate.listIterator(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Object> subList(int fromIndex, int toIndex) {
|
||||
return delegate.subList(fromIndex, toIndex);
|
||||
}
|
||||
// End boiler plate
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,66 +1,70 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
/**
|
||||
* Represents a listener for received or sent packets.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface ChannelListener {
|
||||
|
||||
/**
|
||||
* Invoked when a packet is being sent to the client.
|
||||
* <p>
|
||||
* This is invoked on the main thread.
|
||||
*
|
||||
* @param injector - the channel injector.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the network marker.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the network marker.
|
||||
* @return The packet even that was passed to the listeners, with a possible packet change, or NULL.
|
||||
*/
|
||||
public PacketEvent onPacketSending(Injector injector, Object packet, NetworkMarker marker);
|
||||
|
||||
PacketEvent onPacketSending(Injector injector, Object packet, NetworkMarker marker);
|
||||
|
||||
/**
|
||||
* Invoked when a packet is being received from a client.
|
||||
* <p>
|
||||
* This is invoked on an asynchronous worker thread.
|
||||
*
|
||||
* @param injector - the channel injector.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the associated network marker, if any.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the associated network marker, if any.
|
||||
* @return The packet even that was passed to the listeners, with a possible packet change, or NULL.
|
||||
*/
|
||||
public PacketEvent onPacketReceiving(Injector injector, Object packet, NetworkMarker marker);
|
||||
|
||||
PacketEvent onPacketReceiving(Injector injector, Object packet, NetworkMarker marker);
|
||||
|
||||
/**
|
||||
* Determine if there is a packet listener for the given packet.
|
||||
*
|
||||
* @param packetClass - the packet class to check.
|
||||
* @return TRUE if there is such a listener, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasListener(Class<?> packetClass);
|
||||
|
||||
boolean hasListener(Class<?> packetClass);
|
||||
|
||||
/**
|
||||
* Determine if there is a server packet listener that must be executed on the main thread.
|
||||
*
|
||||
* @param packetClass - the packet class to check.
|
||||
* @return TRUE if there is, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasMainThreadListener(Class<?> packetClass);
|
||||
|
||||
/**
|
||||
* Determine if we need the buffer data of a given client side packet.
|
||||
* @param packetClass - the packet class.
|
||||
* @return TRUE if we do, FALSE otherwise.
|
||||
*/
|
||||
public boolean includeBuffer(Class<?> packetClass);
|
||||
|
||||
boolean hasMainThreadListener(Class<?> packetClass);
|
||||
|
||||
boolean hasMainThreadListener(PacketType type);
|
||||
|
||||
/**
|
||||
* Retrieve the current error reporter.
|
||||
*
|
||||
* @return The error reporter.
|
||||
*/
|
||||
public ErrorReporter getReporter();
|
||||
|
||||
/**
|
||||
* Determine if debug mode is enabled.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
ErrorReporter getReporter();
|
||||
|
||||
/**
|
||||
* Determine if debug mode is enabled.
|
||||
*
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
boolean isDebug();
|
||||
}
|
||||
@@ -1,352 +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.net.SocketAddress;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelMetadata;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelProgressivePromise;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.util.Attribute;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
public abstract class ChannelProxy implements Channel {
|
||||
// Mark that a certain object does not contain a message field
|
||||
private static final FieldAccessor MARK_NO_MESSAGE = new FieldAccessor() {
|
||||
@Override
|
||||
public void set(Object instance, Object value) { }
|
||||
@Override
|
||||
public Object get(Object instance) { return null; }
|
||||
@Override
|
||||
public Field getField() { return null; };
|
||||
};
|
||||
|
||||
// Looking up packets in inner classes
|
||||
private static Map<Class<?>, FieldAccessor> MESSAGE_LOOKUP = Maps.newConcurrentMap();
|
||||
|
||||
// The underlying channel
|
||||
protected Channel delegate;
|
||||
protected Class<?> messageClass;
|
||||
|
||||
// Event loop proxy
|
||||
private transient EventLoopProxy loopProxy;
|
||||
|
||||
public ChannelProxy(Channel delegate, Class<?> messageClass) {
|
||||
this.delegate = delegate;
|
||||
this.messageClass = messageClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a packet is scheduled for transmission in the event loop.
|
||||
* @param <T> Type
|
||||
* @param callable - callable to schedule for execution.
|
||||
* @param packetAccessor - accessor for modifying the packet in the callable.
|
||||
* @return The callable that will be scheduled, or NULL to cancel.
|
||||
*/
|
||||
protected abstract <T> Callable<T> onMessageScheduled(Callable<T> callable, FieldAccessor packetAccessor);
|
||||
|
||||
/**
|
||||
* Invoked when a packet is scheduled for transmission in the event loop.
|
||||
* @param runnable - the runnable that contains a packet to be scheduled.
|
||||
* @param packetAccessor - accessor for modifying the packet in the runnable.
|
||||
* @return The runnable that will be scheduled, or NULL to cancel.
|
||||
*/
|
||||
protected abstract Runnable onMessageScheduled(Runnable runnable, FieldAccessor packetAccessor);
|
||||
|
||||
@Override
|
||||
public <T> Attribute<T> attr(AttributeKey<T> paramAttributeKey) {
|
||||
return delegate.attr(paramAttributeKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture bind(SocketAddress paramSocketAddress) {
|
||||
return delegate.bind(paramSocketAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline pipeline() {
|
||||
return delegate.pipeline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress paramSocketAddress) {
|
||||
return delegate.connect(paramSocketAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBufAllocator alloc() {
|
||||
return delegate.alloc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise newPromise() {
|
||||
return delegate.newPromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventLoop eventLoop() {
|
||||
if (loopProxy == null) {
|
||||
loopProxy = new EventLoopProxy() {
|
||||
@Override
|
||||
protected EventLoop getDelegate() {
|
||||
return delegate.eventLoop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Runnable schedulingRunnable(final Runnable runnable) {
|
||||
final FieldAccessor accessor = getMessageAccessor(runnable);
|
||||
|
||||
if (accessor != null) {
|
||||
Runnable result = onMessageScheduled(runnable, accessor);;
|
||||
return result != null ? result : getEmptyRunnable();
|
||||
}
|
||||
return runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Callable<T> schedulingCallable(Callable<T> callable) {
|
||||
FieldAccessor accessor = getMessageAccessor(callable);
|
||||
|
||||
if (accessor != null) {
|
||||
Callable<T> result = onMessageScheduled(callable, accessor);;
|
||||
return result != null ? result : EventLoopProxy.<T>getEmptyCallable();
|
||||
}
|
||||
return callable;
|
||||
}
|
||||
};
|
||||
}
|
||||
return loopProxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a way to access the packet field of an object.
|
||||
* @param value - the object.
|
||||
* @return The packet field accessor, or NULL if not found.
|
||||
*/
|
||||
private FieldAccessor getMessageAccessor(Object value) {
|
||||
Class<?> clazz = value.getClass();
|
||||
FieldAccessor accessor = MESSAGE_LOOKUP.get(clazz);
|
||||
|
||||
if (accessor == null) {
|
||||
try {
|
||||
accessor = Accessors.getFieldAccessor(clazz, messageClass, true);
|
||||
} catch (IllegalArgumentException e) {
|
||||
accessor = MARK_NO_MESSAGE;
|
||||
}
|
||||
// Save the result
|
||||
MESSAGE_LOOKUP.put(clazz, accessor);
|
||||
}
|
||||
return accessor != MARK_NO_MESSAGE ? accessor : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress paramSocketAddress1,
|
||||
SocketAddress paramSocketAddress2) {
|
||||
return delegate.connect(paramSocketAddress1, paramSocketAddress2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelProgressivePromise newProgressivePromise() {
|
||||
return delegate.newProgressivePromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel parent() {
|
||||
return delegate.parent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelConfig config() {
|
||||
return delegate.config();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture newSucceededFuture() {
|
||||
return delegate.newSucceededFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return delegate.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture disconnect() {
|
||||
return delegate.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered() {
|
||||
return delegate.isRegistered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture newFailedFuture(Throwable paramThrowable) {
|
||||
return delegate.newFailedFuture(paramThrowable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture close() {
|
||||
return delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return delegate.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ChannelFuture deregister() {
|
||||
return delegate.deregister();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise voidPromise() {
|
||||
return delegate.voidPromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelMetadata metadata() {
|
||||
return delegate.metadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture bind(SocketAddress paramSocketAddress,
|
||||
ChannelPromise paramChannelPromise) {
|
||||
return delegate.bind(paramSocketAddress, paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress localAddress() {
|
||||
return delegate.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress remoteAddress() {
|
||||
return delegate.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress paramSocketAddress,
|
||||
ChannelPromise paramChannelPromise) {
|
||||
return delegate.connect(paramSocketAddress, paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture closeFuture() {
|
||||
return delegate.closeFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable() {
|
||||
return delegate.isWritable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel flush() {
|
||||
return delegate.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress paramSocketAddress1,
|
||||
SocketAddress paramSocketAddress2, ChannelPromise paramChannelPromise) {
|
||||
return delegate.connect(paramSocketAddress1, paramSocketAddress2, paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel read() {
|
||||
return delegate.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unsafe unsafe() {
|
||||
return delegate.unsafe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture disconnect(ChannelPromise paramChannelPromise) {
|
||||
return delegate.disconnect(paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture close(ChannelPromise paramChannelPromise) {
|
||||
return delegate.close(paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ChannelFuture deregister(ChannelPromise paramChannelPromise) {
|
||||
return delegate.deregister(paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture write(Object paramObject) {
|
||||
return delegate.write(paramObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture write(Object paramObject, ChannelPromise paramChannelPromise) {
|
||||
return delegate.write(paramObject, paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object paramObject, ChannelPromise paramChannelPromise) {
|
||||
return delegate.writeAndFlush(paramObject, paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object paramObject) {
|
||||
return delegate.writeAndFlush(paramObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Channel o) {
|
||||
return delegate.compareTo(o);
|
||||
}
|
||||
|
||||
/* Added in Netty 4.1, seem to be unused
|
||||
public long bytesBeforeUnwritable() {
|
||||
return delegate.bytesBeforeUnwritable();
|
||||
}
|
||||
|
||||
public long bytesBeforeWritable() {
|
||||
return delegate.bytesBeforeWritable();
|
||||
}
|
||||
|
||||
public ChannelId id() {
|
||||
return delegate.id();
|
||||
}
|
||||
|
||||
public <T> boolean hasAttr(AttributeKey<T> key) {
|
||||
return delegate.hasAttr(key);
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -1,265 +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 io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.util.concurrent.EventExecutor;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.ProgressivePromise;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import io.netty.util.concurrent.ScheduledFuture;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* An event loop proxy.
|
||||
* @author Kristian.
|
||||
*/
|
||||
abstract class EventLoopProxy implements EventLoop {
|
||||
private static final Runnable EMPTY_RUNNABLE = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
private static final Callable<?> EMPTY_CALLABLE = new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return null;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the underlying event loop.
|
||||
* @return The event loop.
|
||||
*/
|
||||
protected abstract EventLoop getDelegate();
|
||||
|
||||
/**
|
||||
* Retrieve a callable that does nothing but return NULL.
|
||||
* @return The empty callable.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Callable<T> getEmptyCallable() {
|
||||
return (Callable<T>) EMPTY_CALLABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a runnable that does nothing.
|
||||
* @return A NO-OP runnable.
|
||||
*/
|
||||
public static Runnable getEmptyRunnable() {
|
||||
return EMPTY_RUNNABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a runnable is being scheduled.
|
||||
* @param runnable - the runnable that is scheduling.
|
||||
* @return The runnable to schedule instead. Cannot be NULL.
|
||||
*/
|
||||
protected abstract Runnable schedulingRunnable(Runnable runnable);
|
||||
|
||||
/**
|
||||
* Invoked when a callable is being scheduled.
|
||||
* @param runnable - the callable that is scheduling.
|
||||
* @return The callable to schedule instead. Cannot be NULL.
|
||||
*/
|
||||
protected abstract <T> Callable<T> schedulingCallable(Callable<T> callable);
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
getDelegate().execute(schedulingRunnable(command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Callable<T> action) {
|
||||
return getDelegate().submit(schedulingCallable(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Runnable action, T arg1) {
|
||||
return getDelegate().submit(schedulingRunnable(action), arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> submit(Runnable action) {
|
||||
return getDelegate().submit(schedulingRunnable(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> ScheduledFuture<V> schedule(Callable<V> action, long arg1, TimeUnit arg2) {
|
||||
return getDelegate().schedule(schedulingCallable(action), arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> schedule(Runnable action, long arg1, TimeUnit arg2) {
|
||||
return getDelegate().schedule(schedulingRunnable(action), arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> scheduleAtFixedRate(Runnable action, long arg1, long arg2, TimeUnit arg3) {
|
||||
return getDelegate().scheduleAtFixedRate(schedulingRunnable(action), arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable action, long arg1, long arg2, TimeUnit arg3) {
|
||||
return getDelegate().scheduleWithFixedDelay(schedulingRunnable(action), arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
// Boiler plate:
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return getDelegate().awaitTermination(timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inEventLoop() {
|
||||
return getDelegate().inEventLoop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inEventLoop(Thread arg0) {
|
||||
return getDelegate().inEventLoop(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return getDelegate().isShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
return getDelegate().isTerminated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<java.util.concurrent.Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
|
||||
throws InterruptedException {
|
||||
return getDelegate().invokeAll(tasks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<java.util.concurrent.Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
|
||||
TimeUnit unit) throws InterruptedException {
|
||||
return getDelegate().invokeAll(tasks, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException,
|
||||
ExecutionException {
|
||||
return getDelegate().invokeAny(tasks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return getDelegate().invokeAny(tasks, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShuttingDown() {
|
||||
return getDelegate().isShuttingDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<EventExecutor> iterator() {
|
||||
return getDelegate().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Future<V> newFailedFuture(Throwable arg0) {
|
||||
return getDelegate().newFailedFuture(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventLoop next() {
|
||||
return ((EventLoopGroup) getDelegate()).next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> ProgressivePromise<V> newProgressivePromise() {
|
||||
return getDelegate().newProgressivePromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Promise<V> newPromise() {
|
||||
return getDelegate().newPromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Future<V> newSucceededFuture(V arg0) {
|
||||
return getDelegate().newSucceededFuture(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventLoopGroup parent() {
|
||||
return getDelegate().parent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture register(Channel arg0, ChannelPromise arg1) {
|
||||
return getDelegate().register(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture register(Channel arg0) {
|
||||
return getDelegate().register(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> shutdownGracefully() {
|
||||
return getDelegate().shutdownGracefully();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> shutdownGracefully(long arg0, long arg1, TimeUnit arg2) {
|
||||
return getDelegate().shutdownGracefully(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> terminationFuture() {
|
||||
return getDelegate().terminationFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void shutdown() {
|
||||
getDelegate().shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public List<Runnable> shutdownNow() {
|
||||
return getDelegate().shutdownNow();
|
||||
}
|
||||
|
||||
/*
|
||||
public ChannelFuture register(ChannelPromise promise) {
|
||||
return getDelegate().register(promise);
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -1,236 +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 io.netty.channel.Channel;
|
||||
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.injector.netty.ChannelInjector.ChannelSocketInjector;
|
||||
import com.comphenix.protocol.injector.server.SocketInjector;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftFields;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
/**
|
||||
* Represents an injector factory.
|
||||
* <p>
|
||||
* Note that the factory will return {@link ClosedInjector} when the factory is closed.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class InjectionFactory {
|
||||
// This should work as long as the injectors are, uh, injected
|
||||
private final ConcurrentMap<Player, Injector> playerLookup = new MapMaker().weakKeys().weakValues().makeMap();
|
||||
private final ConcurrentMap<String, Injector> nameLookup = new MapMaker().weakValues().makeMap();
|
||||
|
||||
// Whether or not the factory is closed
|
||||
private volatile boolean closed;
|
||||
|
||||
// The current plugin
|
||||
private final Plugin plugin;
|
||||
|
||||
public InjectionFactory(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the main plugin associated with this injection factory.
|
||||
* @return The main plugin.
|
||||
*/
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct or retrieve a channel injector from an existing Bukkit player.
|
||||
* @param player - the existing Bukkit player.
|
||||
* @param listener - the listener.
|
||||
* @return A new injector, an existing injector associated with this player, or a closed injector.
|
||||
*/
|
||||
@Nonnull
|
||||
public Injector fromPlayer(Player player, ChannelListener listener) {
|
||||
if (closed)
|
||||
return new ClosedInjector(player);
|
||||
Injector injector = playerLookup.get(player);
|
||||
|
||||
// Find a temporary injector as well
|
||||
if (injector == null)
|
||||
injector = getTemporaryInjector(player);
|
||||
if (injector != null && !injector.isClosed())
|
||||
return injector;
|
||||
|
||||
Object networkManager = MinecraftFields.getNetworkManager(player);
|
||||
|
||||
// Must be a temporary Bukkit player
|
||||
if (networkManager == null) {
|
||||
return fromName(player.getName(), player);
|
||||
}
|
||||
Channel channel = FuzzyReflection.getFieldValue(networkManager, Channel.class, true);
|
||||
|
||||
// See if a channel has already been created
|
||||
injector = (ChannelInjector) ChannelInjector.findChannelHandler(channel, ChannelInjector.class);
|
||||
|
||||
if (injector != null) {
|
||||
// Update the player instance
|
||||
playerLookup.remove(injector.getPlayer());
|
||||
injector.setPlayer(player);
|
||||
} else {
|
||||
injector = new ChannelInjector(player, networkManager, channel, listener, this);
|
||||
}
|
||||
|
||||
// Cache injector and return
|
||||
cacheInjector(player, injector);
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached injector from a name.
|
||||
* <p>
|
||||
* The injector may be NULL if the plugin has been reloaded during a player login.
|
||||
* @param name - the name.
|
||||
* @param player - the player.
|
||||
* @return The cached injector, or a closed injector if it could not be found.
|
||||
*/
|
||||
public Injector fromName(String name, Player player) {
|
||||
if (!closed) {
|
||||
Injector injector = nameLookup.get(name);
|
||||
|
||||
// We can only retrieve cached injectors
|
||||
if (injector != null) {
|
||||
// Update instance
|
||||
injector.setUpdatedPlayer(player);
|
||||
return injector;
|
||||
}
|
||||
}
|
||||
return new ClosedInjector(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new channel injector for the given channel.
|
||||
* @param channel - the channel.
|
||||
* @param listener - the listener.
|
||||
* @param playerFactory - a temporary player creator.
|
||||
* @return The channel injector, or a closed injector.
|
||||
*/
|
||||
@Nonnull
|
||||
public Injector fromChannel(Channel channel, ChannelListener listener, TemporaryPlayerFactory playerFactory) {
|
||||
if (closed)
|
||||
return new ClosedInjector(null);
|
||||
|
||||
Object networkManager = findNetworkManager(channel);
|
||||
Player temporaryPlayer = playerFactory.createTemporaryPlayer(Bukkit.getServer());
|
||||
ChannelInjector injector = new ChannelInjector(temporaryPlayer, networkManager, channel, listener, this);
|
||||
|
||||
// Initialize temporary player
|
||||
TemporaryPlayerFactory.setInjectorInPlayer(temporaryPlayer, new ChannelSocketInjector(injector));
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate a cached injector.
|
||||
* @param player - the associated player.
|
||||
* @return The cached injector, or NULL if nothing was cached.
|
||||
*/
|
||||
public Injector invalidate(Player player) {
|
||||
Injector injector = playerLookup.remove(player);
|
||||
|
||||
nameLookup.remove(player.getName());
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache an injector by player.
|
||||
* @param player - the player.
|
||||
* @param injector - the injector to cache.
|
||||
* @return The previously cached injector.
|
||||
*/
|
||||
public Injector cacheInjector(Player player, Injector injector) {
|
||||
nameLookup.put(player.getName(), injector);
|
||||
return playerLookup.put(player, injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache an injector by name alone.
|
||||
* @param name - the name to lookup.
|
||||
* @param injector - the injector.
|
||||
* @return The cached injector.
|
||||
*/
|
||||
public Injector cacheInjector(String name, Injector injector) {
|
||||
return nameLookup.put(name, injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the associated channel injector.
|
||||
* @param player - the temporary player, or normal Bukkit player.
|
||||
* @return The associated injector, or NULL if this is a Bukkit player.
|
||||
*/
|
||||
private ChannelInjector getTemporaryInjector(Player player) {
|
||||
SocketInjector injector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||
|
||||
if (injector != null) {
|
||||
return ((ChannelSocketInjector) injector).getChannelInjector();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the network manager in a channel's pipeline.
|
||||
* @param channel - the channel.
|
||||
* @return The network manager.
|
||||
*/
|
||||
private Object findNetworkManager(Channel channel) {
|
||||
// Find the network manager
|
||||
Object networkManager = ChannelInjector.findChannelHandler(channel, MinecraftReflection.getNetworkManagerClass());
|
||||
|
||||
if (networkManager != null)
|
||||
return networkManager;
|
||||
throw new IllegalArgumentException("Unable to find NetworkManager in " + channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the factory is closed.
|
||||
* <p>
|
||||
* If it is, all new injectors will be closed by default.
|
||||
* @return TRUE if it is closed, FALSE otherwise.
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all injectors created by this factory, and cease the creation of new injections.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
|
||||
// Close everything
|
||||
for (Injector injector : playerLookup.values())
|
||||
injector.close();
|
||||
for (Injector injector : nameLookup.values())
|
||||
injector.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +1,100 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents an injected client connection.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface Injector {
|
||||
|
||||
/**
|
||||
* Retrieve the current protocol version of the player.
|
||||
*
|
||||
* @return Protocol version.
|
||||
*/
|
||||
public abstract int getProtocolVersion();
|
||||
|
||||
int getProtocolVersion();
|
||||
|
||||
/**
|
||||
* Inject the current channel.
|
||||
* <p>
|
||||
* Note that only active channels can be injected.
|
||||
*
|
||||
* @return TRUE if we injected the channel, false if we could not inject or it was already injected.
|
||||
*/
|
||||
public abstract boolean inject();
|
||||
boolean inject();
|
||||
|
||||
void uninject();
|
||||
|
||||
/**
|
||||
* Close the current injector.
|
||||
*/
|
||||
public abstract void close();
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Send a packet to a player's client.
|
||||
* @param packet - the packet to send.
|
||||
* @param marker - the network marker.
|
||||
*
|
||||
* @param packet - the packet to send.
|
||||
* @param marker - the network marker.
|
||||
* @param filtered - whether or not the packet is filtered.
|
||||
*/
|
||||
public abstract void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered);
|
||||
void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered);
|
||||
|
||||
/**
|
||||
* Recieve a packet on the server.
|
||||
* @param packet - the (NMS) packet to send.
|
||||
*/
|
||||
public abstract void recieveClientPacket(Object packet);
|
||||
void receiveClientPacket(Object packet);
|
||||
|
||||
/**
|
||||
* Retrieve the current protocol state.
|
||||
*
|
||||
* @return The current protocol.
|
||||
*/
|
||||
public abstract Protocol getCurrentProtocol();
|
||||
Protocol getCurrentProtocol();
|
||||
|
||||
/**
|
||||
* Retrieve the network marker associated with a given packet.
|
||||
*
|
||||
* @param packet - the packet.
|
||||
* @return The network marker.
|
||||
*/
|
||||
public abstract NetworkMarker getMarker(Object packet);
|
||||
NetworkMarker getMarker(Object packet);
|
||||
|
||||
/**
|
||||
* Associate a given network marker with a specific packet.
|
||||
*
|
||||
* @param packet - the NMS packet.
|
||||
* @param marker - the associated marker.
|
||||
*/
|
||||
public abstract void saveMarker(Object packet, NetworkMarker marker);
|
||||
void saveMarker(Object packet, NetworkMarker marker);
|
||||
|
||||
/**
|
||||
* Retrieve the current player or temporary player associated with the injector.
|
||||
*
|
||||
* @return The current player.
|
||||
*/
|
||||
public abstract Player getPlayer();
|
||||
Player getPlayer();
|
||||
|
||||
/**
|
||||
* Set the current player instance.
|
||||
*
|
||||
* @param player - the current player.
|
||||
*/
|
||||
public abstract void setPlayer(Player player);
|
||||
|
||||
void setPlayer(Player player);
|
||||
|
||||
void disconnect(String message);
|
||||
|
||||
/**
|
||||
* Determine if the channel has already been injected.
|
||||
*
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
public abstract boolean isInjected();
|
||||
boolean isInjected();
|
||||
|
||||
/**
|
||||
* Determine if this channel has been closed and cleaned up.
|
||||
*
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
public abstract boolean isClosed();
|
||||
|
||||
/**
|
||||
* Set the updated player instance.
|
||||
* <p>
|
||||
* This will not replace the current instance, but it will allow PacketEvent to provide additional player information.
|
||||
* @param player - the more up-to-date player.
|
||||
*/
|
||||
public abstract void setUpdatedPlayer(Player player);
|
||||
boolean isClosed();
|
||||
}
|
||||
@@ -1,25 +1,26 @@
|
||||
/**
|
||||
* 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
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2015 dmulloy2
|
||||
* <p>
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
* <p>
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
* <p>
|
||||
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import io.netty.buffer.AbstractByteBuf;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -33,35 +34,31 @@ import java.nio.channels.GatheringByteChannel;
|
||||
import java.nio.channels.ScatteringByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
/**
|
||||
* Construct a ByteBuf around an input stream and an output stream.
|
||||
* <p>
|
||||
* Note that as streams usually don't support seeking, this implementation will ignore
|
||||
* all indexing in the byte buffer.
|
||||
* Note that as streams usually don't support seeking, this implementation will ignore all indexing in the byte buffer.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
private DataInputStream input;
|
||||
private DataOutputStream output;
|
||||
|
||||
|
||||
private static final int CAPACITY = Integer.MAX_VALUE;
|
||||
|
||||
// For modifying the reader or writer index
|
||||
private static FieldAccessor READER_INDEX;
|
||||
private static FieldAccessor WRITER_INDEX;
|
||||
|
||||
private static final int CAPACITY = Short.MAX_VALUE;
|
||||
|
||||
|
||||
private final DataInputStream input;
|
||||
private final DataOutputStream output;
|
||||
|
||||
private NettyByteBufAdapter(DataInputStream input, DataOutputStream output) {
|
||||
// Just pick a figure
|
||||
super(CAPACITY);
|
||||
this.input = input;
|
||||
this.output = output;
|
||||
|
||||
|
||||
// Prepare accessors
|
||||
try {
|
||||
if (READER_INDEX == null) {
|
||||
@@ -73,32 +70,37 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot initialize ByteBufAdapter.", e);
|
||||
}
|
||||
|
||||
|
||||
// "Infinite" reading/writing
|
||||
if (input == null)
|
||||
if (input == null) {
|
||||
READER_INDEX.set(this, Integer.MAX_VALUE);
|
||||
if (output == null)
|
||||
}
|
||||
|
||||
if (output == null) {
|
||||
WRITER_INDEX.set(this, Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new Minecraft packet serializer using the current byte buf adapter.
|
||||
*
|
||||
* @param input - the input stream.
|
||||
* @return A packet serializer with a wrapped byte buf adapter.
|
||||
*/
|
||||
public static ByteBuf packetReader(DataInputStream input) {
|
||||
return (ByteBuf) MinecraftReflection.getPacketDataSerializer(new NettyByteBufAdapter(input, null));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new Minecraft packet deserializer using the current byte buf adapter.
|
||||
*
|
||||
* @param output - the output stream.
|
||||
* @return A packet serializer with a wrapped byte buf adapter.
|
||||
*/
|
||||
public static ByteBuf packetWriter(DataOutputStream output) {
|
||||
return (ByteBuf) MinecraftReflection.getPacketDataSerializer(new NettyByteBufAdapter(null, output));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int refCnt() {
|
||||
return 1;
|
||||
@@ -117,7 +119,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
protected byte _getByte(int paramInt) {
|
||||
try {
|
||||
return input.readByte();
|
||||
return this.input.readByte();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read input.", e);
|
||||
}
|
||||
@@ -126,7 +128,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
protected short _getShort(int paramInt) {
|
||||
try {
|
||||
return input.readShort();
|
||||
return this.input.readShort();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read input.", e);
|
||||
}
|
||||
@@ -135,7 +137,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
protected int _getUnsignedMedium(int paramInt) {
|
||||
try {
|
||||
return input.readUnsignedShort();
|
||||
return this.input.readUnsignedShort();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read input.", e);
|
||||
}
|
||||
@@ -144,7 +146,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
protected int _getInt(int paramInt) {
|
||||
try {
|
||||
return input.readInt();
|
||||
return this.input.readInt();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read input.", e);
|
||||
}
|
||||
@@ -153,7 +155,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
protected long _getLong(int paramInt) {
|
||||
try {
|
||||
return input.readLong();
|
||||
return this.input.readLong();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read input.", e);
|
||||
}
|
||||
@@ -162,7 +164,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
protected void _setByte(int index, int value) {
|
||||
try {
|
||||
output.writeByte(value);
|
||||
this.output.writeByte(value);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot write output.", e);
|
||||
}
|
||||
@@ -171,7 +173,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
protected void _setShort(int index, int value) {
|
||||
try {
|
||||
output.writeShort(value);
|
||||
this.output.writeShort(value);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot write output.", e);
|
||||
}
|
||||
@@ -180,7 +182,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
protected void _setMedium(int index, int value) {
|
||||
try {
|
||||
output.writeShort(value);
|
||||
this.output.writeShort(value);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot write output.", e);
|
||||
}
|
||||
@@ -189,7 +191,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
protected void _setInt(int index, int value) {
|
||||
try {
|
||||
output.writeInt(value);
|
||||
this.output.writeInt(value);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot write output.", e);
|
||||
}
|
||||
@@ -198,7 +200,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
protected void _setLong(int index, long value) {
|
||||
try {
|
||||
output.writeLong(value);
|
||||
this.output.writeLong(value);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot write output.", e);
|
||||
}
|
||||
@@ -238,7 +240,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
|
||||
try {
|
||||
for (int i = 0; i < length; i++) {
|
||||
dst.setByte(dstIndex + i, input.read());
|
||||
dst.setByte(dstIndex + i, this.input.read());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read input.", e);
|
||||
@@ -249,7 +251,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
|
||||
try {
|
||||
input.read(dst, dstIndex, length);
|
||||
this.input.read(dst, dstIndex, length);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read input.", e);
|
||||
}
|
||||
@@ -259,7 +261,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
public ByteBuf getBytes(int index, ByteBuffer dst) {
|
||||
try {
|
||||
dst.put(ByteStreams.toByteArray(input));
|
||||
dst.put(ByteStreams.toByteArray(this.input));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read input.", e);
|
||||
}
|
||||
@@ -268,14 +270,14 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
|
||||
@Override
|
||||
public ByteBuf getBytes(int index, OutputStream dst, int length) throws IOException {
|
||||
ByteStreams.copy(ByteStreams.limit(input, length), dst);
|
||||
ByteStreams.copy(ByteStreams.limit(this.input, length), dst);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
|
||||
byte[] data = ByteStreams.toByteArray(ByteStreams.limit(input, length));
|
||||
|
||||
byte[] data = ByteStreams.toByteArray(ByteStreams.limit(this.input, length));
|
||||
|
||||
out.write(ByteBuffer.wrap(data));
|
||||
return data.length;
|
||||
}
|
||||
@@ -284,9 +286,9 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {
|
||||
byte[] buffer = new byte[length];
|
||||
src.getBytes(srcIndex, buffer);
|
||||
|
||||
|
||||
try {
|
||||
output.write(buffer);
|
||||
this.output.write(buffer);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot write output.", e);
|
||||
@@ -296,7 +298,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
|
||||
try {
|
||||
output.write(src, srcIndex, length);
|
||||
this.output.write(src, srcIndex, length);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot write output.", e);
|
||||
@@ -306,7 +308,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
public ByteBuf setBytes(int index, ByteBuffer src) {
|
||||
try {
|
||||
WritableByteChannel channel = Channels.newChannel(output);
|
||||
WritableByteChannel channel = Channels.newChannel(this.output);
|
||||
|
||||
channel.write(src);
|
||||
return this;
|
||||
@@ -318,15 +320,15 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
@Override
|
||||
public int setBytes(int index, InputStream in, int length) throws IOException {
|
||||
InputStream limit = ByteStreams.limit(in, length);
|
||||
ByteStreams.copy(limit, output);
|
||||
ByteStreams.copy(limit, this.output);
|
||||
return length - limit.available();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(length);
|
||||
WritableByteChannel channel = Channels.newChannel(output);
|
||||
|
||||
WritableByteChannel channel = Channels.newChannel(this.output);
|
||||
|
||||
int count = in.read(buffer);
|
||||
channel.write(buffer);
|
||||
return count;
|
||||
@@ -424,7 +426,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int setBytes(int arg0, FileChannel arg1, long arg2, int arg3) throws IOException {
|
||||
public int setBytes(int arg0, FileChannel arg1, long arg2, int arg3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
|
||||
public class NettyNetworkMarker extends NetworkMarker {
|
||||
public NettyNetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer) {
|
||||
super(side, inputBuffer, null);
|
||||
}
|
||||
|
||||
public NettyNetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer) {
|
||||
super(side, inputBuffer, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream skipHeader(DataInputStream input) throws IOException {
|
||||
// Skip the variable int containing the packet ID
|
||||
getSerializer().deserializeVarInt(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteBuffer addHeader(ByteBuffer buffer, PacketType type) {
|
||||
// We don't have to add anything - it's already there
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream addHeader(DataInputStream input, PacketType type) {
|
||||
// As above
|
||||
return input;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +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.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* Stores packets that need to be sent without being handled by the listeners (filtered=false).
|
||||
* When other packets sent after sending the packet are removed, the packet is removed as well
|
||||
* to prevent a memory leak, assuming a consistent send order is in place.
|
||||
*
|
||||
* @author bergerkiller
|
||||
*/
|
||||
public class PacketFilterQueue {
|
||||
private Queue<Object> queue = new ArrayDeque<>();
|
||||
|
||||
/**
|
||||
* Adds a packet to this queue, indicating further on that it should not be filtered.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
public synchronized void add(Object packet) {
|
||||
queue.add(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a packet is contained inside this queue, indicating
|
||||
* it should not be filtered.
|
||||
*
|
||||
* @param packet
|
||||
* @return True if contained and packet should not be filtered (filtered=false)
|
||||
*/
|
||||
public synchronized boolean contains(Object packet) {
|
||||
return queue.contains(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a packet is contained inside this queue and removes it if so.
|
||||
* Other packets marked in this queue that were sent before this packet are
|
||||
* removed from the queue also, avoiding memory leaks because of dropped packets.
|
||||
*
|
||||
* @param packet
|
||||
* @return True if contained and packet should not be filtered (filtered=false)
|
||||
*/
|
||||
public synchronized boolean remove(Object packet) {
|
||||
if (queue.isEmpty()) {
|
||||
// Nothing in the queue
|
||||
return false;
|
||||
} else if (queue.peek() == packet) {
|
||||
// First in the queue (expected)
|
||||
queue.poll();
|
||||
return true;
|
||||
} else if (!queue.contains(packet)) {
|
||||
// There are unfiltered packets, but this one is not
|
||||
return false;
|
||||
} else {
|
||||
// We have skipped over some packets (unexpected)
|
||||
// Poll packets until we find it
|
||||
while (queue.poll() != packet) {
|
||||
if (queue.isEmpty()) {
|
||||
// This should never happen! But to avoid infinite loop.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,388 +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.net.SocketAddress;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import io.netty.channel.*;
|
||||
import io.netty.util.concurrent.EventExecutorGroup;
|
||||
|
||||
/**
|
||||
* A pipeline proxy.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PipelineProxy implements ChannelPipeline {
|
||||
protected final ChannelPipeline pipeline;
|
||||
protected final Channel channel;
|
||||
|
||||
public PipelineProxy(ChannelPipeline pipeline, Channel channel) {
|
||||
this.pipeline = pipeline;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addAfter(EventExecutorGroup arg0, String arg1, String arg2, ChannelHandler arg3) {
|
||||
pipeline.addAfter(arg0, arg1, arg2, arg3);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addAfter(String arg0, String arg1, ChannelHandler arg2) {
|
||||
pipeline.addAfter(arg0, arg1, arg2);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addBefore(EventExecutorGroup arg0, String arg1, String arg2, ChannelHandler arg3) {
|
||||
pipeline.addBefore(arg0, arg1, arg2, arg3);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addBefore(String arg0, String arg1, ChannelHandler arg2) {
|
||||
pipeline.addBefore(arg0, arg1, arg2);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addFirst(ChannelHandler... arg0) {
|
||||
pipeline.addFirst(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addFirst(EventExecutorGroup arg0, ChannelHandler... arg1) {
|
||||
pipeline.addFirst(arg0, arg1);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addFirst(EventExecutorGroup arg0, String arg1, ChannelHandler arg2) {
|
||||
pipeline.addFirst(arg0, arg1, arg2);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addFirst(String arg0, ChannelHandler arg1) {
|
||||
pipeline.addFirst(arg0, arg1);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addLast(ChannelHandler... arg0) {
|
||||
pipeline.addLast(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addLast(EventExecutorGroup arg0, ChannelHandler... arg1) {
|
||||
pipeline.addLast(arg0, arg1);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addLast(EventExecutorGroup arg0, String arg1, ChannelHandler arg2) {
|
||||
pipeline.addLast(arg0, arg1, arg2);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addLast(String arg0, ChannelHandler arg1) {
|
||||
pipeline.addLast(arg0, arg1);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture bind(SocketAddress arg0, ChannelPromise arg1) {
|
||||
return pipeline.bind(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture bind(SocketAddress arg0) {
|
||||
return pipeline.bind(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel channel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture close() {
|
||||
return pipeline.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture close(ChannelPromise arg0) {
|
||||
return pipeline.close(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress arg0, ChannelPromise arg1) {
|
||||
return pipeline.connect(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress arg0, SocketAddress arg1, ChannelPromise arg2) {
|
||||
return pipeline.connect(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress arg0, SocketAddress arg1) {
|
||||
return pipeline.connect(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress arg0) {
|
||||
return pipeline.connect(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandlerContext context(ChannelHandler arg0) {
|
||||
return pipeline.context(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandlerContext context(Class<? extends ChannelHandler> arg0) {
|
||||
return pipeline.context(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandlerContext context(String arg0) {
|
||||
return pipeline.context(arg0);
|
||||
}
|
||||
|
||||
// We have to call the depreciated methods to properly implement the proxy
|
||||
@Override
|
||||
public ChannelFuture deregister() {
|
||||
return pipeline.deregister();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture deregister(ChannelPromise arg0) {
|
||||
return pipeline.deregister(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelUnregistered() {
|
||||
pipeline.fireChannelUnregistered();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture disconnect() {
|
||||
return pipeline.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture disconnect(ChannelPromise arg0) {
|
||||
return pipeline.disconnect(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelActive() {
|
||||
pipeline.fireChannelActive();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelInactive() {
|
||||
pipeline.fireChannelInactive();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelRead(Object arg0) {
|
||||
pipeline.fireChannelRead(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelReadComplete() {
|
||||
pipeline.fireChannelReadComplete();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelRegistered() {
|
||||
pipeline.fireChannelRegistered();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelWritabilityChanged() {
|
||||
pipeline.fireChannelWritabilityChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireExceptionCaught(Throwable arg0) {
|
||||
pipeline.fireExceptionCaught(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireUserEventTriggered(Object arg0) {
|
||||
pipeline.fireUserEventTriggered(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler first() {
|
||||
return pipeline.first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandlerContext firstContext() {
|
||||
return pipeline.firstContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline flush() {
|
||||
pipeline.flush();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ChannelHandler> T get(Class<T> arg0) {
|
||||
return pipeline.get(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler get(String arg0) {
|
||||
return pipeline.get(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<String, ChannelHandler>> iterator() {
|
||||
return pipeline.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler last() {
|
||||
return pipeline.last();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandlerContext lastContext() {
|
||||
return pipeline.lastContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> names() {
|
||||
return pipeline.names();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline read() {
|
||||
pipeline.read();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline remove(ChannelHandler arg0) {
|
||||
pipeline.remove(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ChannelHandler> T remove(Class<T> arg0) {
|
||||
return pipeline.remove(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler remove(String arg0) {
|
||||
return pipeline.remove(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler removeFirst() {
|
||||
return pipeline.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler removeLast() {
|
||||
return pipeline.removeLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline replace(ChannelHandler arg0, String arg1, ChannelHandler arg2) {
|
||||
pipeline.replace(arg0, arg1, arg2);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ChannelHandler> T replace(Class<T> arg0, String arg1, ChannelHandler arg2) {
|
||||
return pipeline.replace(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler replace(String arg0, String arg1, ChannelHandler arg2) {
|
||||
return pipeline.replace(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ChannelHandler> toMap() {
|
||||
return pipeline.toMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture write(Object arg0, ChannelPromise arg1) {
|
||||
return pipeline.write(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture write(Object arg0) {
|
||||
return pipeline.write(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object arg0, ChannelPromise arg1) {
|
||||
return pipeline.writeAndFlush(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object arg0) {
|
||||
return pipeline.writeAndFlush(arg0);
|
||||
}
|
||||
|
||||
/* Added in Netty 4.1, seem to be unused
|
||||
public ChannelFuture newFailedFuture(Throwable ex) {
|
||||
return pipeline.newFailedFuture(ex);
|
||||
}
|
||||
|
||||
public ChannelProgressivePromise newProgressivePromise() {
|
||||
return pipeline.newProgressivePromise();
|
||||
}
|
||||
|
||||
public ChannelPromise newPromise() {
|
||||
return pipeline.newPromise();
|
||||
}
|
||||
|
||||
public ChannelFuture newSucceededFuture() {
|
||||
return pipeline.newSucceededFuture();
|
||||
}
|
||||
|
||||
public ChannelPromise voidPromise() {
|
||||
return pipeline.voidPromise();
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -1,467 +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.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
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;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandler;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
|
||||
public class ProtocolInjector implements ChannelListener {
|
||||
public static final ReportType REPORT_CANNOT_INJECT_INCOMING_CHANNEL = new ReportType("Unable to inject incoming channel %s.");
|
||||
|
||||
private volatile boolean injected;
|
||||
private volatile boolean closed;
|
||||
|
||||
// The temporary player factory
|
||||
private TemporaryPlayerFactory playerFactory = new TemporaryPlayerFactory();
|
||||
private List<VolatileField> bootstrapFields = Lists.newArrayList();
|
||||
|
||||
// The channel injector factory
|
||||
private InjectionFactory injectionFactory;
|
||||
|
||||
// List of network managers
|
||||
private volatile List<Object> networkManagers;
|
||||
|
||||
// Different sending filters
|
||||
private PacketTypeSet sendingFilters = new PacketTypeSet();
|
||||
private PacketTypeSet reveivedFilters = new PacketTypeSet();
|
||||
|
||||
// Packets that must be executed on the main thread
|
||||
private PacketTypeSet mainThreadFilters = new PacketTypeSet();
|
||||
|
||||
// Which packets are buffered
|
||||
private PacketTypeSet bufferedPackets = new PacketTypeSet();
|
||||
private ListenerInvoker invoker;
|
||||
|
||||
// Handle errors
|
||||
private ErrorReporter reporter;
|
||||
private boolean debug;
|
||||
|
||||
public ProtocolInjector(Plugin plugin, ListenerInvoker invoker, ErrorReporter reporter) {
|
||||
this.injectionFactory = new InjectionFactory(plugin);
|
||||
this.invoker = invoker;
|
||||
this.reporter = reporter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not the debug mode is enabled.
|
||||
* @param debug - TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject into the spigot connection class.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized void inject() {
|
||||
if (injected)
|
||||
throw new IllegalStateException("Cannot inject twice.");
|
||||
try {
|
||||
FuzzyReflection fuzzyServer = FuzzyReflection.fromClass(MinecraftReflection.getMinecraftServerClass());
|
||||
List<Method> serverConnectionMethods = fuzzyServer.getMethodListByParameters(MinecraftReflection.getServerConnectionClass(), new Class[] {});
|
||||
|
||||
// Get the server connection
|
||||
Object server = fuzzyServer.getSingleton();
|
||||
Object serverConnection = null;
|
||||
|
||||
for (Method method : serverConnectionMethods) {
|
||||
try {
|
||||
serverConnection = method.invoke(server);
|
||||
|
||||
// Continue until we get a server connection
|
||||
if (serverConnection != null) {
|
||||
break;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ProtocolLogger.debug("Encountered an exception invoking " + method, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (serverConnection == null) {
|
||||
throw new ReflectiveOperationException("Failed to obtain server connection");
|
||||
}
|
||||
|
||||
// Handle connected channels
|
||||
final ChannelInboundHandler endInitProtocol = new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(final Channel channel) throws Exception {
|
||||
try {
|
||||
synchronized (networkManagers) {
|
||||
// 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
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
reporter.reportDetailed(ProtocolInjector.this, Report.newBuilder(REPORT_CANNOT_INJECT_INCOMING_CHANNEL).messageParam(channel).error(ex));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This is executed before Minecraft's channel handler
|
||||
final ChannelInboundHandler beginInitProtocol = new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel channel) throws Exception {
|
||||
// Our only job is to add init protocol
|
||||
channel.pipeline().addLast(endInitProtocol);
|
||||
}
|
||||
};
|
||||
|
||||
// Add our handler to newly created channels
|
||||
final ChannelHandler connectionHandler = new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
Channel channel = (Channel) msg;
|
||||
|
||||
// Prepare to initialize ths channel
|
||||
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);
|
||||
|
||||
try {
|
||||
Field field = fuzzy.getParameterizedField(List.class, MinecraftReflection.getNetworkManagerClass());
|
||||
field.setAccessible(true);
|
||||
|
||||
networkManagers = (List<Object>) field.get(serverConnection);
|
||||
} catch (Exception ex) {
|
||||
ProtocolLogger.debug("Encountered an exception checking list fields", ex);
|
||||
|
||||
Method method = fuzzy.getMethodByParameters("getNetworkManagers", List.class,
|
||||
new Class<?>[] { serverConnection.getClass() });
|
||||
method.setAccessible(true);
|
||||
|
||||
networkManagers = (List<Object>) method.invoke(null, serverConnection);
|
||||
}
|
||||
|
||||
if (networkManagers == null) {
|
||||
throw new ReflectiveOperationException("Failed to obtain list of network managers");
|
||||
}
|
||||
|
||||
// Insert ProtocolLib's connection interceptor
|
||||
bootstrapFields = getBootstrapFields(serverConnection);
|
||||
|
||||
for (VolatileField field : bootstrapFields) {
|
||||
final List<Object> list = (List<Object>) field.getValue();
|
||||
|
||||
// We don't have to override this list
|
||||
if (list == networkManagers) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Synchronize with each list before we attempt to replace them.
|
||||
field.setValue(new BootstrapList(list, connectionHandler));
|
||||
}
|
||||
|
||||
injected = true;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to inject channel futures.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasListener(Class<?> packetClass) {
|
||||
return reveivedFilters.contains(packetClass) || sendingFilters.contains(packetClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainThreadListener(Class<?> packetClass) {
|
||||
return mainThreadFilters.contains(packetClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorReporter getReporter() {
|
||||
return reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject our packet handling into a specific player.
|
||||
* @param player Player to inject into
|
||||
*/
|
||||
public void injectPlayer(Player player) {
|
||||
injectionFactory.fromPlayer(player, this).inject();
|
||||
}
|
||||
|
||||
private List<VolatileField> getBootstrapFields(Object serverConnection) {
|
||||
List<VolatileField> result = Lists.newArrayList();
|
||||
|
||||
// Find and (possibly) proxy every list
|
||||
for (Field field : FuzzyReflection.fromObject(serverConnection, true).getFieldListByType(List.class)) {
|
||||
VolatileField volatileField = new VolatileField(field, serverConnection, true).toSynchronized();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> list = (List<Object>) volatileField.getValue();
|
||||
|
||||
if (list.size() == 0 || list.get(0) instanceof ChannelFuture) {
|
||||
result.add(volatileField);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up any remaning injections.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
|
||||
for (VolatileField field : bootstrapFields) {
|
||||
Object value = field.getValue();
|
||||
|
||||
// Undo the processed channels, if any
|
||||
if (value instanceof BootstrapList) {
|
||||
((BootstrapList) value).close();
|
||||
}
|
||||
field.revertValue();
|
||||
}
|
||||
// Uninject all the players
|
||||
injectionFactory.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent onPacketSending(Injector injector, Object packet, NetworkMarker marker) {
|
||||
Class<?> clazz = packet.getClass();
|
||||
|
||||
if (sendingFilters.contains(clazz) || marker != null) {
|
||||
try {
|
||||
PacketContainer container = new PacketContainer(PacketRegistry.getPacketType(clazz), packet);
|
||||
return packetQueued(container, injector.getPlayer(), marker);
|
||||
} catch (LinkageError e) {
|
||||
System.err.println("[ProtocolLib] Encountered a LinkageError (likely a misbehaving wrapper), please report this!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't change anything
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent onPacketReceiving(Injector injector, Object packet, NetworkMarker marker) {
|
||||
Class<?> clazz = packet.getClass();
|
||||
|
||||
if (reveivedFilters.contains(clazz) || marker != null) {
|
||||
PacketContainer container = new PacketContainer(PacketRegistry.getPacketType(clazz), packet);
|
||||
return packetReceived(container, injector.getPlayer(), marker);
|
||||
}
|
||||
|
||||
// Don't change anything
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeBuffer(Class<?> packetClass) {
|
||||
return bufferedPackets.contains(packetClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to inform the event listeners of a queued packet.
|
||||
* @param packet - the packet that is to be sent.
|
||||
* @param receiver - the receiver of this packet.
|
||||
* @return The packet event that was used.
|
||||
*/
|
||||
private PacketEvent packetQueued(PacketContainer packet, Player receiver, NetworkMarker marker) {
|
||||
PacketEvent event = PacketEvent.fromServer(this, packet, marker, receiver);
|
||||
|
||||
invoker.invokePacketSending(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to inform the event listeners of a received packet.
|
||||
* @param packet - the packet that has been receieved.
|
||||
* @param sender - the client packet.
|
||||
* @param marker - the network marker.
|
||||
* @return The packet event that was used.
|
||||
*/
|
||||
private PacketEvent packetReceived(PacketContainer packet, Player sender, NetworkMarker marker) {
|
||||
PacketEvent event = PacketEvent.fromClient(this, packet, marker, sender);
|
||||
|
||||
invoker.invokePacketRecieving(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
// Server side
|
||||
public PlayerInjectionHandler getPlayerInjector() {
|
||||
return new AbstractPlayerHandler(sendingFilters) {
|
||||
private ChannelListener listener = ProtocolInjector.this;
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(Player player) {
|
||||
return injectionFactory.fromPlayer(player, listener).getProtocolVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePlayer(Player player) {
|
||||
injectionFactory.fromPlayer(player, listener).inject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectPlayer(Player player, ConflictStrategy strategy) {
|
||||
injectionFactory.fromPlayer(player, listener).inject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uninjectPlayer(InetSocketAddress address) {
|
||||
// Ignore this too
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
||||
if (!type.isAsyncForced() && (options == null || !options.contains(ListenerOptions.ASYNC)))
|
||||
mainThreadFilters.addType(type);
|
||||
super.addPacketHandler(type, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePacketHandler(PacketType type) {
|
||||
mainThreadFilters.removeType(type);
|
||||
super.removePacketHandler(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uninjectPlayer(Player player) {
|
||||
// Just let Netty clean this up
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
|
||||
injectionFactory.fromPlayer(receiver, listener).sendServerPacket(packet.getHandle(), marker, filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainThreadListener(PacketType type) {
|
||||
return mainThreadFilters.contains(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
|
||||
injectionFactory.fromPlayer(player, listener).recieveClientPacket(mcPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent handlePacketRecieved(PacketContainer packet, InputStream input, byte[] buffered) {
|
||||
// Ignore this
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDisconnect(Player player) {
|
||||
injectionFactory.fromPlayer(player, listener).close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel(Player player) {
|
||||
Injector injector = injectionFactory.fromPlayer(player, listener);
|
||||
if (injector instanceof ChannelInjector) {
|
||||
return ((ChannelInjector) injector).getChannel();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a view of this protocol injector as a packet injector.
|
||||
* @return The packet injector.
|
||||
*/
|
||||
// Client side
|
||||
public PacketInjector getPacketInjector() {
|
||||
return new AbstractPacketInjector(reveivedFilters) {
|
||||
@Override
|
||||
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
|
||||
NetworkMarker marker = buffered != null ? new NettyNetworkMarker(ConnectionSide.CLIENT_SIDE, buffered) : null;
|
||||
injectionFactory.fromPlayer(client, ProtocolInjector.this).saveMarker(packet.getHandle(), marker);
|
||||
return packetReceived(packet, client, marker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inputBuffersChanged(Set<PacketType> set) {
|
||||
bufferedPackets = new PacketTypeSet(set);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,268 +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 io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
|
||||
/**
|
||||
* This class wraps a Netty {@link Channel} in a {@link Socket}. It overrides
|
||||
* all methods in {@link Socket} to ensure that calls are not mistakingly made
|
||||
* to the unsupported super socket. All operations that can be sanely applied to
|
||||
* a {@link Channel} are implemented here. Those which cannot will throw an
|
||||
* {@link UnsupportedOperationException}.
|
||||
*/
|
||||
// Thanks MD5. :)
|
||||
public class SocketAdapter extends Socket {
|
||||
private final SocketChannel ch;
|
||||
|
||||
private SocketAdapter(SocketChannel ch) {
|
||||
this.ch = ch;
|
||||
}
|
||||
|
||||
public static SocketAdapter adapt(Channel ch) {
|
||||
if (!(ch instanceof SocketChannel)) {
|
||||
return null;
|
||||
}
|
||||
return new SocketAdapter((SocketChannel) ch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress bindpoint) throws IOException {
|
||||
ch.bind(bindpoint).syncUninterruptibly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
ch.close().syncUninterruptibly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(SocketAddress endpoint) throws IOException {
|
||||
ch.connect(endpoint).syncUninterruptibly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(SocketAddress endpoint, int timeout) throws IOException {
|
||||
ch.config().setConnectTimeoutMillis(timeout);
|
||||
ch.connect(endpoint).syncUninterruptibly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof SocketAdapter && ch.equals(((SocketAdapter) obj).ch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.nio.channels.SocketChannel getChannel() {
|
||||
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getInetAddress() {
|
||||
return ch.remoteAddress().getAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getKeepAlive() throws SocketException {
|
||||
return ch.config().getOption(ChannelOption.SO_KEEPALIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getLocalAddress() {
|
||||
return ch.localAddress().getAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalPort() {
|
||||
return ch.localAddress().getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalSocketAddress() {
|
||||
return ch.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getOOBInline() throws SocketException {
|
||||
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return ch.remoteAddress().getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getReceiveBufferSize() throws SocketException {
|
||||
return ch.config().getOption(ChannelOption.SO_RCVBUF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteSocketAddress() {
|
||||
return ch.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getReuseAddress() throws SocketException {
|
||||
return ch.config().getOption(ChannelOption.SO_REUSEADDR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getSendBufferSize() throws SocketException {
|
||||
return ch.config().getOption(ChannelOption.SO_SNDBUF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSoLinger() throws SocketException {
|
||||
return ch.config().getOption(ChannelOption.SO_LINGER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getSoTimeout() throws SocketException {
|
||||
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getTcpNoDelay() throws SocketException {
|
||||
return ch.config().getOption(ChannelOption.TCP_NODELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrafficClass() throws SocketException {
|
||||
return ch.config().getOption(ChannelOption.IP_TOS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ch.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBound() {
|
||||
return ch.localAddress() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return !ch.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return ch.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInputShutdown() {
|
||||
return ch.isInputShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutputShutdown() {
|
||||
return ch.isOutputShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendUrgentData(int data) throws IOException {
|
||||
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeepAlive(boolean on) throws SocketException {
|
||||
ch.config().setOption(ChannelOption.SO_KEEPALIVE, on);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOOBInline(boolean on) throws SocketException {
|
||||
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
|
||||
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setReceiveBufferSize(int size) throws SocketException {
|
||||
ch.config().setOption(ChannelOption.SO_RCVBUF, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReuseAddress(boolean on) throws SocketException {
|
||||
ch.config().setOption(ChannelOption.SO_REUSEADDR, on);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setSendBufferSize(int size) throws SocketException {
|
||||
ch.config().setOption(ChannelOption.SO_SNDBUF, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoLinger(boolean on, int linger) throws SocketException {
|
||||
ch.config().setOption(ChannelOption.SO_LINGER, linger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setSoTimeout(int timeout) throws SocketException {
|
||||
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTcpNoDelay(boolean on) throws SocketException {
|
||||
ch.config().setOption(ChannelOption.TCP_NODELAY, on);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTrafficClass(int tc) throws SocketException {
|
||||
ch.config().setOption(ChannelOption.IP_TOS, tc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownInput() throws IOException {
|
||||
throw new UnsupportedOperationException("Operation not supported on Channel wrapper.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownOutput() throws IOException {
|
||||
ch.shutdownOutput().syncUninterruptibly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ch.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,263 +1,241 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2015 dmulloy2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
/**
|
||||
* A packet represented only by its id and bytes.
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class WirePacket {
|
||||
private final int id;
|
||||
private final byte[] bytes;
|
||||
|
||||
/**
|
||||
* Constructs a new WirePacket with a given type and contents
|
||||
* @param type Type of the packet
|
||||
* @param bytes Contents of the packet
|
||||
*/
|
||||
public WirePacket(PacketType type, byte[] bytes) {
|
||||
this.id = checkNotNull(type, "type cannot be null").getCurrentId();
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new WirePacket with a given id and contents
|
||||
* @param id ID of the packet
|
||||
* @param bytes Contents of the packet
|
||||
*/
|
||||
public WirePacket(int id, byte[] bytes) {
|
||||
this.id = id;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this packet's ID
|
||||
* @return The ID
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this packet's contents as a byte array
|
||||
* @return The contents
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the id of this packet to a given output
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeId(ByteBuf output) {
|
||||
writeVarInt(output, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the contents of this packet to a given output
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeBytes(ByteBuf output) {
|
||||
checkNotNull(output, "output cannot be null!");
|
||||
output.writeBytes(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully writes the ID and contents of this packet to a given output
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeFully(ByteBuf output) {
|
||||
writeId(output);
|
||||
writeBytes(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this packet into a byte buffer
|
||||
* @return The buffer
|
||||
*/
|
||||
public ByteBuf serialize() {
|
||||
ByteBuf buffer = Unpooled.buffer();
|
||||
writeFully(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
|
||||
if (obj instanceof WirePacket) {
|
||||
WirePacket that = (WirePacket) obj;
|
||||
return this.id == that.id &&
|
||||
Arrays.equals(this.bytes, that.bytes);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(bytes);
|
||||
result = prime * result + id;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WirePacket[id=" + id + ", bytes=" + Arrays.toString(bytes) + "]";
|
||||
}
|
||||
|
||||
private static byte[] getBytes(ByteBuf buffer) {
|
||||
byte[] array = new byte[buffer.readableBytes()];
|
||||
buffer.readBytes(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WirePacket from an existing PacketContainer
|
||||
* @param packet Existing packet
|
||||
* @return The resulting WirePacket
|
||||
*/
|
||||
public static WirePacket fromPacket(PacketContainer packet) {
|
||||
int id = packet.getType().getCurrentId();
|
||||
return new WirePacket(id, bytesFromPacket(packet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a byte array from an existing PacketContainer containing all the
|
||||
* bytes from that packet
|
||||
*
|
||||
* @param packet Existing packet
|
||||
* @return the byte array
|
||||
*/
|
||||
public static byte[] bytesFromPacket(PacketContainer packet) {
|
||||
checkNotNull(packet, "packet cannot be null!");
|
||||
|
||||
ByteBuf buffer = PacketContainer.createPacketBuffer();
|
||||
ByteBuf store = PacketContainer.createPacketBuffer();
|
||||
|
||||
// Read the bytes once
|
||||
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
|
||||
|
||||
try {
|
||||
write.invoke(packet.getHandle(), buffer);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to read packet contents.", ex);
|
||||
}
|
||||
|
||||
byte[] bytes = getBytes(buffer);
|
||||
|
||||
buffer.release();
|
||||
|
||||
// Rewrite them to the packet to avoid issues with certain packets
|
||||
if (packet.getType() == PacketType.Play.Server.CUSTOM_PAYLOAD
|
||||
|| packet.getType() == PacketType.Play.Client.CUSTOM_PAYLOAD) {
|
||||
// Make a copy of the array before writing
|
||||
byte[] ret = Arrays.copyOf(bytes, bytes.length);
|
||||
store.writeBytes(bytes);
|
||||
|
||||
Method read = MinecraftMethods.getPacketReadByteBufMethod();
|
||||
|
||||
try {
|
||||
read.invoke(packet.getHandle(), store);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to rewrite packet contents.", ex);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
store.release();
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WirePacket from an existing Minecraft packet
|
||||
* @param packet Existing Minecraft packet
|
||||
* @return The resulting WirePacket
|
||||
* @throws IllegalArgumentException If the packet is null or not a Minecraft packet
|
||||
*/
|
||||
public static WirePacket fromPacket(Object packet) {
|
||||
checkNotNull(packet, "packet cannot be null!");
|
||||
checkArgument(MinecraftReflection.isPacketClass(packet), "packet must be a Minecraft packet");
|
||||
|
||||
PacketType type = PacketType.fromClass(packet.getClass());
|
||||
int id = type.getCurrentId();
|
||||
|
||||
ByteBuf buffer = PacketContainer.createPacketBuffer();
|
||||
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
|
||||
|
||||
try {
|
||||
write.invoke(packet, buffer);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to serialize packet contents.", ex);
|
||||
}
|
||||
|
||||
byte[] bytes = getBytes(buffer);
|
||||
|
||||
buffer.release();
|
||||
|
||||
return new WirePacket(id, bytes);
|
||||
}
|
||||
|
||||
public static void writeVarInt(ByteBuf output, int i) {
|
||||
checkNotNull(output, "output cannot be null!");
|
||||
|
||||
while ((i & -128) != 0) {
|
||||
output.writeByte(i & 127 | 128);
|
||||
i >>>= 7;
|
||||
}
|
||||
|
||||
output.writeByte(i);
|
||||
}
|
||||
|
||||
public static int readVarInt(ByteBuf input) {
|
||||
checkNotNull(input, "input cannot be null!");
|
||||
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
byte b0;
|
||||
|
||||
do {
|
||||
b0 = input.readByte();
|
||||
i |= (b0 & 127) << j++ * 7;
|
||||
if (j > 5) {
|
||||
throw new RuntimeException("VarInt too big");
|
||||
}
|
||||
} while ((b0 & 128) == 128);
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2015 dmulloy2
|
||||
* <p>
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
* <p>
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
* <p>
|
||||
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.StreamSerializer;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A packet represented only by its id and bytes.
|
||||
*
|
||||
* @author dmulloy2
|
||||
*/
|
||||
@SuppressWarnings("deprecation") // yea we need to do that :/
|
||||
public class WirePacket {
|
||||
|
||||
private final int id;
|
||||
private final byte[] bytes;
|
||||
|
||||
/**
|
||||
* Constructs a new WirePacket with a given type and contents
|
||||
*
|
||||
* @param type Type of the packet
|
||||
* @param bytes Contents of the packet
|
||||
*/
|
||||
public WirePacket(PacketType type, byte[] bytes) {
|
||||
this.id = checkNotNull(type, "type cannot be null").getCurrentId();
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new WirePacket with a given id and contents
|
||||
*
|
||||
* @param id ID of the packet
|
||||
* @param bytes Contents of the packet
|
||||
*/
|
||||
public WirePacket(int id, byte[] bytes) {
|
||||
this.id = id;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WirePacket from an existing PacketContainer
|
||||
*
|
||||
* @param packet Existing packet
|
||||
* @return The resulting WirePacket
|
||||
*/
|
||||
public static WirePacket fromPacket(PacketContainer packet) {
|
||||
int id = packet.getType().getCurrentId();
|
||||
return new WirePacket(id, bytesFromPacket(packet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a byte array from an existing PacketContainer containing all the bytes from that packet
|
||||
*
|
||||
* @param packet Existing packet
|
||||
* @return the byte array
|
||||
*/
|
||||
public static byte[] bytesFromPacket(PacketContainer packet) {
|
||||
checkNotNull(packet, "packet cannot be null!");
|
||||
|
||||
ByteBuf buffer = PacketContainer.createPacketBuffer();
|
||||
ByteBuf store = PacketContainer.createPacketBuffer();
|
||||
|
||||
// Read the bytes once
|
||||
MethodAccessor write = MinecraftMethods.getPacketWriteByteBufMethod();
|
||||
write.invoke(packet.getHandle(), buffer);
|
||||
|
||||
byte[] bytes = StreamSerializer.getDefault().getBytesAndRelease(buffer);
|
||||
|
||||
// Rewrite them to the packet to avoid issues with certain packets
|
||||
if (packet.getType() == PacketType.Play.Server.CUSTOM_PAYLOAD
|
||||
|| packet.getType() == PacketType.Play.Client.CUSTOM_PAYLOAD) {
|
||||
// Make a copy of the array before writing
|
||||
byte[] ret = Arrays.copyOf(bytes, bytes.length);
|
||||
store.writeBytes(bytes);
|
||||
|
||||
MethodAccessor read = MinecraftMethods.getPacketReadByteBufMethod();
|
||||
read.invoke(packet.getHandle(), store);
|
||||
|
||||
bytes = ret;
|
||||
}
|
||||
|
||||
ReferenceCountUtil.safeRelease(store);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WirePacket from an existing Minecraft packet
|
||||
*
|
||||
* @param packet Existing Minecraft packet
|
||||
* @return The resulting WirePacket
|
||||
* @throws IllegalArgumentException If the packet is null or not a Minecraft packet
|
||||
*/
|
||||
public static WirePacket fromPacket(Object packet) {
|
||||
checkNotNull(packet, "packet cannot be null!");
|
||||
checkArgument(MinecraftReflection.isPacketClass(packet), "packet must be a Minecraft packet");
|
||||
|
||||
ByteBuf buffer = PacketContainer.createPacketBuffer();
|
||||
|
||||
MethodAccessor write = MinecraftMethods.getPacketWriteByteBufMethod();
|
||||
write.invoke(packet, buffer);
|
||||
|
||||
byte[] bytes = StreamSerializer.getDefault().getBytesAndRelease(buffer);
|
||||
int id = PacketType.fromClass(packet.getClass()).getCurrentId();
|
||||
|
||||
return new WirePacket(id, bytes);
|
||||
}
|
||||
|
||||
public static void writeVarInt(ByteBuf output, int value) {
|
||||
while (true) {
|
||||
if ((value & ~0x7F) == 0) {
|
||||
output.writeByte(value);
|
||||
break;
|
||||
} else {
|
||||
output.writeByte((value & 0x7F) | 0x80);
|
||||
value >>>= 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int readVarInt(ByteBuf input) {
|
||||
int result = 0;
|
||||
for (byte j = 0; j < 5; j++) {
|
||||
int nextByte = input.readByte();
|
||||
result |= (nextByte & 0x7F) << j * 7;
|
||||
if ((nextByte & 0x80) != 128) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("VarInt is too big");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this packet's ID
|
||||
*
|
||||
* @return The ID
|
||||
*/
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this packet's contents as a byte array
|
||||
*
|
||||
* @return The contents
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
return this.bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the id of this packet to a given output
|
||||
*
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeId(ByteBuf output) {
|
||||
writeVarInt(output, this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the contents of this packet to a given output
|
||||
*
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeBytes(ByteBuf output) {
|
||||
checkNotNull(output, "output cannot be null!");
|
||||
output.writeBytes(this.bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully writes the ID and contents of this packet to a given output
|
||||
*
|
||||
* @param output Output to write to
|
||||
*/
|
||||
public void writeFully(ByteBuf output) {
|
||||
this.writeId(output);
|
||||
this.writeBytes(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this packet into a byte buffer
|
||||
*
|
||||
* @return The buffer
|
||||
*/
|
||||
public ByteBuf serialize() {
|
||||
ByteBuf buffer = Unpooled.buffer();
|
||||
this.writeFully(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj instanceof WirePacket) {
|
||||
WirePacket that = (WirePacket) obj;
|
||||
return this.id == that.id && Arrays.equals(this.bytes, that.bytes);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(this.bytes);
|
||||
result = prime * result + this.id;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WirePacket[id=" + this.id + ", bytes=" + Arrays.toString(this.bytes) + "]";
|
||||
}
|
||||
}
|
||||
+24
-30
@@ -1,43 +1,44 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
final class EmptyInjector implements Injector {
|
||||
|
||||
public static final Injector WITHOUT_PLAYER = new EmptyInjector(null);
|
||||
|
||||
/**
|
||||
* Represents a closed injector.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ClosedInjector implements Injector {
|
||||
private Player player;
|
||||
|
||||
/**
|
||||
* Construct a new injector that is always closed.
|
||||
* @param player - the associated player.
|
||||
*/
|
||||
public ClosedInjector(Player player) {
|
||||
public EmptyInjector(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inject() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninject() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Object packet) {
|
||||
// Do nothing
|
||||
public void receiveClientPacket(Object packet) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,24 +53,22 @@ public class ClosedInjector implements Injector {
|
||||
|
||||
@Override
|
||||
public void saveMarker(Object packet, NetworkMarker marker) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpdatedPlayer(Player player) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
return this.player;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInjected() {
|
||||
return false;
|
||||
@@ -79,9 +78,4 @@ public class ClosedInjector implements Injector {
|
||||
public boolean isClosed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
final class InboundPacketInterceptor extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final NettyChannelInjector injector;
|
||||
private final ChannelListener channelListener;
|
||||
|
||||
public InboundPacketInterceptor(NettyChannelInjector injector, ChannelListener listener) {
|
||||
this.injector = injector;
|
||||
this.channelListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
Class<?> messageClass = msg.getClass();
|
||||
if (this.shouldInterceptMessage(messageClass)) {
|
||||
// process the login if the packet is one before posting the packet to any handler to provide "real" data
|
||||
// the method invocation will do nothing if the packet is not a login packet
|
||||
this.injector.tryProcessLogin(msg);
|
||||
|
||||
// check if there are any listeners bound for the packet - if not just post the packet down the pipeline
|
||||
if (!this.channelListener.hasListener(messageClass)) {
|
||||
ctx.fireChannelRead(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// call all inbound listeners
|
||||
this.injector.processInboundPacket(ctx, msg, messageClass);
|
||||
} else {
|
||||
// just pass the message down the pipeline
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldInterceptMessage(Class<?> messageClass) {
|
||||
// only intercept minecraft packets and no garbage from other stuff in the channel
|
||||
return MinecraftReflection.getPacketClass().isAssignableFrom(messageClass);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2015 dmulloy2
|
||||
* <p>
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
* <p>
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
* <p>
|
||||
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
import com.comphenix.protocol.injector.temporary.MinimalInjector;
|
||||
import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftFields;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import javax.annotation.Nonnull;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents an injector factory.
|
||||
* <p>
|
||||
* Note that the factory will return {@link EmptyInjector} when the factory is closed.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class InjectionFactory {
|
||||
|
||||
// This should work as long as the injectors are, uh, injected
|
||||
private final ConcurrentMap<String, Injector> nameLookup = new MapMaker().weakValues().makeMap();
|
||||
private final ConcurrentMap<Player, Injector> playerLookup = new MapMaker().weakKeys().weakValues().makeMap();
|
||||
|
||||
// bukkit stuff
|
||||
private final Plugin plugin;
|
||||
private final Server server;
|
||||
|
||||
// protocol lib stuff
|
||||
private final ErrorReporter errorReporter;
|
||||
|
||||
// state of the factory
|
||||
private boolean closed;
|
||||
|
||||
public InjectionFactory(Plugin plugin, Server server, ErrorReporter errorReporter) {
|
||||
this.plugin = plugin;
|
||||
this.server = server;
|
||||
this.errorReporter = errorReporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the main plugin associated with this injection factory.
|
||||
*
|
||||
* @return The main plugin.
|
||||
*/
|
||||
public Plugin getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct or retrieve a channel injector from an existing Bukkit player.
|
||||
*
|
||||
* @param player - the existing Bukkit player.
|
||||
* @param listener - the listener.
|
||||
* @return A new injector, an existing injector associated with this player, or a closed injector.
|
||||
*/
|
||||
@Nonnull
|
||||
public Injector fromPlayer(Player player, ChannelListener listener) {
|
||||
if (this.closed) {
|
||||
return new EmptyInjector(player);
|
||||
}
|
||||
|
||||
// try to get the injector using the player reference first
|
||||
Injector injector = this.playerLookup.get(player);
|
||||
if (injector == null) {
|
||||
injector = this.getTemporaryInjector(player);
|
||||
}
|
||||
|
||||
// check if we found an injector
|
||||
if (injector != null && !injector.isClosed()) {
|
||||
return injector;
|
||||
}
|
||||
|
||||
// check if a network manager is present, if not maybe we cached the player temporarily
|
||||
Object networkManager = MinecraftFields.getNetworkManager(player);
|
||||
if (networkManager == null) {
|
||||
return this.fromName(player.getName(), player);
|
||||
}
|
||||
|
||||
// get the channel of the player and check if we already hooked into it
|
||||
Channel channel = FuzzyReflection.getFieldValue(networkManager, Channel.class, true);
|
||||
injector = NettyChannelInjector.findInjector(channel);
|
||||
|
||||
if (injector != null) {
|
||||
// check if the new player is not the old one, this saves us a bit when many calls to the method are made
|
||||
if (injector.getPlayer() != player || !this.playerLookup.containsKey(player)) {
|
||||
this.playerLookup.remove(injector.getPlayer());
|
||||
this.cacheInjector(player, injector);
|
||||
// re-set the player of the injection
|
||||
injector.setPlayer(player);
|
||||
}
|
||||
} else {
|
||||
// construct a new injector as it seems like we have none yet
|
||||
injector = new NettyChannelInjector(
|
||||
player,
|
||||
this.server,
|
||||
networkManager,
|
||||
channel,
|
||||
listener,
|
||||
this,
|
||||
this.errorReporter);
|
||||
this.cacheInjector(player, injector);
|
||||
}
|
||||
|
||||
// definitely not null
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached injector from a name.
|
||||
* <p>
|
||||
* The injector may be NULL if the plugin has been reloaded during a player login.
|
||||
*
|
||||
* @param name - the name.
|
||||
* @param player - the player.
|
||||
* @return The cached injector, or a closed injector if it could not be found.
|
||||
*/
|
||||
public Injector fromName(String name, Player player) {
|
||||
if (this.closed) {
|
||||
return new EmptyInjector(player);
|
||||
}
|
||||
|
||||
// check if we have a player with that name cached
|
||||
Injector injector = this.nameLookup.get(name);
|
||||
if (injector != null) {
|
||||
injector.setPlayer(player);
|
||||
return injector;
|
||||
}
|
||||
|
||||
return new EmptyInjector(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new channel injector for the given channel.
|
||||
*
|
||||
* @param channel - the channel.
|
||||
* @param listener - the listener.
|
||||
* @param playerFactory - a temporary player creator.
|
||||
* @return The channel injector, or a closed injector.
|
||||
*/
|
||||
@Nonnull
|
||||
public Injector fromChannel(Channel channel, ChannelListener listener, TemporaryPlayerFactory playerFactory) {
|
||||
if (this.closed) {
|
||||
return EmptyInjector.WITHOUT_PLAYER;
|
||||
}
|
||||
|
||||
Object netManager = this.findNetworkManager(channel);
|
||||
Player temporaryPlayer = playerFactory.createTemporaryPlayer(this.server);
|
||||
|
||||
NettyChannelInjector injector = new NettyChannelInjector(
|
||||
temporaryPlayer,
|
||||
this.server,
|
||||
netManager,
|
||||
channel,
|
||||
listener,
|
||||
this,
|
||||
this.errorReporter);
|
||||
MinimalInjector minimalInjector = new NettyChannelMinimalInjector(injector);
|
||||
|
||||
// Initialize temporary player
|
||||
TemporaryPlayerFactory.setInjectorInPlayer(temporaryPlayer, minimalInjector);
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate a cached injector.
|
||||
*
|
||||
* @param player - the associated player.
|
||||
* @return The cached injector, or NULL if nothing was cached.
|
||||
*/
|
||||
public Injector invalidate(Player player, String name) {
|
||||
Injector injector = null;
|
||||
|
||||
// try the name first, more unsafe but works 99% of the time
|
||||
if (name != null) {
|
||||
injector = this.nameLookup.remove(name);
|
||||
}
|
||||
|
||||
// if we have a player then use that as the safe removal way
|
||||
if (player != null) {
|
||||
injector = this.playerLookup.remove(player);
|
||||
}
|
||||
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache an injector by player.
|
||||
*
|
||||
* @param player - the player.
|
||||
* @param injector - the injector to cache.
|
||||
* @return The previously cached injector.
|
||||
*/
|
||||
public Injector cacheInjector(Player player, Injector injector) {
|
||||
this.nameLookup.put(player.getName(), injector);
|
||||
return this.playerLookup.put(player, injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache an injector by name alone.
|
||||
*
|
||||
* @param name - the name to lookup.
|
||||
* @param injector - the injector.
|
||||
* @return The cached injector.
|
||||
*/
|
||||
public Injector cacheInjector(String name, Injector injector) {
|
||||
return this.nameLookup.put(name, injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the associated channel injector.
|
||||
*
|
||||
* @param player - the temporary player, or normal Bukkit player.
|
||||
* @return The associated injector, or NULL if this is a Bukkit player.
|
||||
*/
|
||||
private NettyChannelInjector getTemporaryInjector(Player player) {
|
||||
MinimalInjector injector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||
if (injector instanceof NettyChannelMinimalInjector) {
|
||||
return ((NettyChannelMinimalInjector) injector).getInjector();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the network manager in a channel's pipeline.
|
||||
*
|
||||
* @param channel - the channel.
|
||||
* @return The network manager.
|
||||
*/
|
||||
private Object findNetworkManager(Channel channel) {
|
||||
// Find the network manager
|
||||
Object networkManager = NettyChannelInjector.findChannelHandler(channel,
|
||||
MinecraftReflection.getNetworkManagerClass());
|
||||
if (networkManager != null) {
|
||||
return networkManager;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unable to find NetworkManager in " + channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the factory is closed.
|
||||
* <p>
|
||||
* If it is, all new injectors will be closed by default.
|
||||
*
|
||||
* @return TRUE if it is closed, FALSE otherwise.
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return this.closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all injectors created by this factory, and cease the creation of new injections.
|
||||
*/
|
||||
public void close() {
|
||||
if (!this.closed) {
|
||||
this.closed = true;
|
||||
|
||||
// Close everything
|
||||
for (Injector injector : this.playerLookup.values()) {
|
||||
injector.close();
|
||||
}
|
||||
|
||||
for (Injector injector : this.nameLookup.values()) {
|
||||
injector.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+662
@@ -0,0 +1,662 @@
|
||||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.NetworkProcessor;
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
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.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.utility.ByteBuddyGenerated;
|
||||
import com.comphenix.protocol.utility.MinecraftFields;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.util.AttributeKey;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class NettyChannelInjector implements Injector {
|
||||
|
||||
// an accessor used when we're unable to retrieve the actual packet field in an outbound packet send
|
||||
private static final FieldAccessor NO_OP_ACCESSOR = new FieldAccessor() {
|
||||
@Override
|
||||
public Object get(Object instance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object instance, Object value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Field getField() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private static final String INTERCEPTOR_NAME = "protocol_lib_inbound_interceptor";
|
||||
private static final String WIRE_PACKET_ENCODER_NAME = "protocol_lib_wire_packet_encoder";
|
||||
|
||||
// all registered channel handlers to easier make sure we unregister them all from the pipeline
|
||||
private static final String[] PROTOCOL_LIB_HANDLERS = new String[]{
|
||||
WIRE_PACKET_ENCODER_NAME, INTERCEPTOR_NAME
|
||||
};
|
||||
|
||||
private static final ReportType REPORT_CANNOT_SEND_PACKET = new ReportType("Unable to send packet %s to %s");
|
||||
|
||||
private static final WirePacketEncoder WIRE_PACKET_ENCODER = new WirePacketEncoder();
|
||||
private static final Map<Class<?>, FieldAccessor> PACKET_ACCESSORS = new ConcurrentHashMap<>(16, 0.9f);
|
||||
|
||||
private static final Class<?> LOGIN_PACKET_START_CLASS = PacketType.Login.Client.START.getPacketClass();
|
||||
private static final Class<?> PACKET_PROTOCOL_CLASS = PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass();
|
||||
|
||||
private static final AttributeKey<Integer> PROTOCOL_VERSION = AttributeKey.valueOf(getRandomKey());
|
||||
private static final AttributeKey<NettyChannelInjector> INJECTOR = AttributeKey.valueOf(getRandomKey());
|
||||
|
||||
// lazy initialized fields, if we don't need them we don't bother about them
|
||||
private static FieldAccessor LOGIN_PROFILE_ACCESSOR;
|
||||
private static FieldAccessor PROTOCOL_VERSION_ACCESSOR;
|
||||
|
||||
// bukkit stuff
|
||||
private final Server server;
|
||||
|
||||
// protocol lib stuff we need
|
||||
private final ErrorReporter errorReporter;
|
||||
private final NetworkProcessor networkProcessor;
|
||||
|
||||
// references
|
||||
private final Object networkManager;
|
||||
private final Channel wrappedChannel;
|
||||
private final ChannelListener channelListener;
|
||||
private final InjectionFactory injectionFactory;
|
||||
|
||||
private final FieldAccessor channelField;
|
||||
|
||||
// packet marking
|
||||
private final Map<Object, NetworkMarker> savedMarkers = new WeakHashMap<>(16, 0.9f);
|
||||
private final Set<Object> skippedPackets = Collections.synchronizedSet(new HashSet<>());
|
||||
protected final ThreadLocal<Boolean> processedPackets = ThreadLocal.withInitial(() -> Boolean.FALSE);
|
||||
|
||||
// status of this injector
|
||||
private volatile boolean closed = false;
|
||||
private volatile boolean injected = false;
|
||||
|
||||
// information about the player belonging to this injector
|
||||
private String playerName;
|
||||
private Player resolvedPlayer;
|
||||
|
||||
// lazy initialized fields, if we don't need them we don't bother about them
|
||||
private Object playerConnection;
|
||||
private FieldAccessor protocolAccessor;
|
||||
|
||||
public NettyChannelInjector(
|
||||
Player player,
|
||||
Server server,
|
||||
Object netManager,
|
||||
Channel channel,
|
||||
ChannelListener listener,
|
||||
InjectionFactory injector,
|
||||
ErrorReporter errorReporter
|
||||
) {
|
||||
// bukkit stuff
|
||||
this.server = server;
|
||||
this.resolvedPlayer = player;
|
||||
|
||||
// protocol lib stuff
|
||||
this.errorReporter = errorReporter;
|
||||
this.networkProcessor = new NetworkProcessor(errorReporter);
|
||||
|
||||
// references
|
||||
this.networkManager = netManager;
|
||||
this.wrappedChannel = channel;
|
||||
this.channelListener = listener;
|
||||
this.injectionFactory = injector;
|
||||
|
||||
// register us into the channel
|
||||
this.wrappedChannel.attr(INJECTOR).set(this);
|
||||
|
||||
// read the channel field from the network manager given to this method
|
||||
// we re-read this field every time as plugins/spigot forks might give us different network manager types
|
||||
Field channelField = FuzzyReflection.fromObject(netManager, true).getField(FuzzyFieldContract.newBuilder()
|
||||
.typeExact(Channel.class)
|
||||
.banModifier(Modifier.STATIC)
|
||||
.build());
|
||||
this.channelField = Accessors.getFieldAccessor(channelField);
|
||||
|
||||
// hook here into the close future to be 100% sure that this injector gets closed when the channel we wrap gets closed
|
||||
// normally we listen to the disconnect event, but there is a very small period of time, between the login and actual
|
||||
// join that is not covered by the disconnect event and may lead to unexpected injector states...
|
||||
this.wrappedChannel.closeFuture().addListener(future -> this.close());
|
||||
}
|
||||
|
||||
static NettyChannelInjector findInjector(Channel channel) {
|
||||
return channel.attr(INJECTOR).get();
|
||||
}
|
||||
|
||||
static Object findChannelHandler(Channel channel, Class<?> type) {
|
||||
for (Entry<String, ChannelHandler> entry : channel.pipeline()) {
|
||||
if (type.isAssignableFrom(entry.getValue().getClass())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getRandomKey() {
|
||||
return "ProtocolLib-" + ThreadLocalRandom.current().nextLong();
|
||||
}
|
||||
|
||||
private static boolean hasProtocolLibHandler(Channel channel) {
|
||||
for (String handler : PROTOCOL_LIB_HANDLERS) {
|
||||
if (channel.pipeline().get(handler) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion() {
|
||||
Integer protocolVersion = this.wrappedChannel.attr(PROTOCOL_VERSION).get();
|
||||
return protocolVersion == null ? MinecraftProtocolVersion.getCurrentVersion() : protocolVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inject() {
|
||||
// we only do this on the channel event loop to prevent blocking the main server thread
|
||||
// and to be sure that the netty pipeline view we get is up-to-date
|
||||
if (this.wrappedChannel.eventLoop().inEventLoop()) {
|
||||
// ensure that we should actually inject into the channel
|
||||
if (this.closed || this.wrappedChannel instanceof ByteBuddyGenerated || !this.wrappedChannel.isActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check here if we need to rewrite the channel field and do so
|
||||
// minecraft overrides the channel field when the channel actually becomes active, so we need to ensure that our
|
||||
// proxied channel is always on that field - therefore this rewrite is event before we check if we're already
|
||||
// injected into the channel
|
||||
this.rewriteChannelField();
|
||||
|
||||
// check if we already injected into the channel
|
||||
if (hasProtocolLibHandler(this.wrappedChannel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// inject our handlers
|
||||
this.wrappedChannel.pipeline().addAfter("encoder", WIRE_PACKET_ENCODER_NAME, WIRE_PACKET_ENCODER);
|
||||
this.wrappedChannel.pipeline().addAfter(
|
||||
"decoder",
|
||||
INTERCEPTOR_NAME,
|
||||
new InboundPacketInterceptor(this, this.channelListener));
|
||||
|
||||
this.injected = true;
|
||||
return true;
|
||||
} else {
|
||||
// re-run in event loop, return false as we cannot be sure if the injection actually worked
|
||||
this.ensureInEventLoop(this::inject);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninject() {
|
||||
// ensure that we injected into the channel before trying to remove anything from it
|
||||
if (this.injected) {
|
||||
// uninject on the event loop to ensure the instant visibility of the change and prevent blocks of other threads
|
||||
if (this.wrappedChannel.eventLoop().inEventLoop()) {
|
||||
this.injected = false;
|
||||
|
||||
// remove known references to us
|
||||
this.wrappedChannel.attr(INJECTOR).remove();
|
||||
this.channelField.set(this.networkManager, this.wrappedChannel);
|
||||
|
||||
for (String handler : PROTOCOL_LIB_HANDLERS) {
|
||||
try {
|
||||
this.wrappedChannel.pipeline().remove(handler);
|
||||
} catch (NoSuchElementException ignored) {
|
||||
// ignore that one, probably an edge case
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.ensureInEventLoop(this::uninject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// ensure that the injector wasn't close before
|
||||
if (!this.closed) {
|
||||
this.closed = true;
|
||||
|
||||
// remove all of our references from the channel
|
||||
this.uninject();
|
||||
|
||||
// cleanup
|
||||
this.savedMarkers.clear();
|
||||
this.skippedPackets.clear();
|
||||
|
||||
// wipe this injector completely
|
||||
this.injectionFactory.invalidate(this.getPlayer(), this.playerName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
// do not send the packet if this injector was already closed / is not injected yet
|
||||
if (this.closed || !this.injected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// register the packet as filtered if we shouldn't post it to any listener
|
||||
if (!filtered) {
|
||||
this.skippedPackets.add(packet);
|
||||
}
|
||||
|
||||
// save the given packet marker and send the packet
|
||||
this.saveMarker(packet, marker);
|
||||
try {
|
||||
if (this.resolvedPlayer instanceof ByteBuddyGenerated) {
|
||||
MinecraftMethods.getNetworkManagerHandleMethod().invoke(this.networkManager, packet);
|
||||
} else {
|
||||
// ensure that the player is properly connected before sending
|
||||
Object playerConnection = this.getPlayerConnection();
|
||||
if (playerConnection != null) {
|
||||
MinecraftMethods.getSendPacketMethod().invoke(playerConnection, packet);
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
this.errorReporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_SEND_PACKET)
|
||||
.messageParam(packet, this.playerName)
|
||||
.error(exception)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveClientPacket(Object packet) {
|
||||
// do not do that if we're not injected or this injector was closed
|
||||
if (this.closed || !this.injected) {
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable receiveAction = () -> {
|
||||
try {
|
||||
// try to invoke the method, this should normally not fail
|
||||
MinecraftMethods.getNetworkManagerReadPacketMethod().invoke(this.networkManager, null, packet);
|
||||
} catch (Exception exception) {
|
||||
// 99% the user gave wrong information to the server
|
||||
this.errorReporter.reportMinimal(this.injectionFactory.getPlugin(), "receiveClientPacket", exception);
|
||||
}
|
||||
};
|
||||
|
||||
// execute the action on the event loop rather than any thread which we should potentially not block
|
||||
if (this.wrappedChannel.eventLoop().inEventLoop()) {
|
||||
receiveAction.run();
|
||||
} else {
|
||||
this.ensureInEventLoop(receiveAction);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Protocol getCurrentProtocol() {
|
||||
// ensure that the accessor to the protocol field is available
|
||||
if (this.protocolAccessor == null) {
|
||||
this.protocolAccessor = Accessors.getFieldAccessor(
|
||||
this.networkManager.getClass(),
|
||||
MinecraftReflection.getEnumProtocolClass(),
|
||||
true);
|
||||
}
|
||||
|
||||
Object nmsProtocol = this.protocolAccessor.get(this.networkManager);
|
||||
return Protocol.fromVanilla((Enum<?>) nmsProtocol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkMarker getMarker(Object packet) {
|
||||
return this.savedMarkers.get(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveMarker(Object packet, NetworkMarker marker) {
|
||||
if (marker != null && !this.closed) {
|
||||
this.savedMarkers.put(packet, marker);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
// if the player was already resolved there is no need to do further lookups
|
||||
if (this.resolvedPlayer != null) {
|
||||
return this.resolvedPlayer;
|
||||
}
|
||||
|
||||
// check if the name of the player is already known to the injector
|
||||
if (this.playerName != null) {
|
||||
this.resolvedPlayer = this.server.getPlayerExact(this.playerName);
|
||||
}
|
||||
|
||||
// either we resolved it or we didn't...
|
||||
return this.resolvedPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayer(Player player) {
|
||||
this.resolvedPlayer = player;
|
||||
this.playerName = player.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(String message) {
|
||||
// we're still during pre-login, just close the connection
|
||||
if (this.playerConnection == null || this.resolvedPlayer instanceof ByteBuddyGenerated) {
|
||||
this.wrappedChannel.disconnect();
|
||||
} else {
|
||||
try {
|
||||
// try to call the disconnect method on the player
|
||||
MinecraftMethods.getDisconnectMethod(this.playerConnection.getClass()).invoke(this.playerConnection, message);
|
||||
} catch (Exception exception) {
|
||||
throw new IllegalArgumentException("Unable to disconnect the current injector", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInjected() {
|
||||
return this.injected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return this.closed;
|
||||
}
|
||||
|
||||
void tryProcessLogin(Object packet) {
|
||||
// check if the given packet is a login packet
|
||||
if (LOGIN_PACKET_START_CLASS != null && LOGIN_PACKET_START_CLASS.equals(packet.getClass())) {
|
||||
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
|
||||
// 1.19 removed the profile from the packet and now sends the plain username directly
|
||||
// ensure that the game profile accessor is available
|
||||
if (LOGIN_PROFILE_ACCESSOR == null) {
|
||||
LOGIN_PROFILE_ACCESSOR = Accessors.getFieldAccessor(LOGIN_PACKET_START_CLASS, String.class, true);
|
||||
}
|
||||
|
||||
// get the username from the packet
|
||||
String username = (String) LOGIN_PROFILE_ACCESSOR.get(packet);
|
||||
|
||||
// cache the injector and the player name
|
||||
this.playerName = username;
|
||||
this.injectionFactory.cacheInjector(username, this);
|
||||
} else {
|
||||
// ensure that the game profile accessor is available
|
||||
if (LOGIN_PROFILE_ACCESSOR == null) {
|
||||
LOGIN_PROFILE_ACCESSOR = Accessors.getFieldAccessor(
|
||||
LOGIN_PACKET_START_CLASS,
|
||||
MinecraftReflection.getGameProfileClass(),
|
||||
true);
|
||||
}
|
||||
|
||||
// the client only sends the name but the server wraps it into a GameProfile, so here we are
|
||||
WrappedGameProfile profile = WrappedGameProfile.fromHandle(LOGIN_PROFILE_ACCESSOR.get(packet));
|
||||
|
||||
// cache the injector and the player name
|
||||
this.playerName = profile.getName();
|
||||
this.injectionFactory.cacheInjector(profile.getName(), this);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// protocol version begin
|
||||
if (PACKET_PROTOCOL_CLASS != null && PACKET_PROTOCOL_CLASS.equals(packet.getClass())) {
|
||||
// ensure the protocol version accessor is available
|
||||
if (PROTOCOL_VERSION_ACCESSOR == null) {
|
||||
try {
|
||||
Field ver = FuzzyReflection.fromClass(PACKET_PROTOCOL_CLASS, true).getField(FuzzyFieldContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.typeExact(int.class)
|
||||
.build());
|
||||
PROTOCOL_VERSION_ACCESSOR = Accessors.getFieldAccessor(ver);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
// unable to resolve that field, continue no-op
|
||||
PROTOCOL_VERSION_ACCESSOR = NO_OP_ACCESSOR;
|
||||
}
|
||||
}
|
||||
|
||||
// read the protocol version from the field if available
|
||||
if (PROTOCOL_VERSION_ACCESSOR != NO_OP_ACCESSOR) {
|
||||
int protocolVersion = (int) PROTOCOL_VERSION_ACCESSOR.get(packet);
|
||||
this.wrappedChannel.attr(PROTOCOL_VERSION).set(protocolVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rewriteChannelField() {
|
||||
// check if we need to rewrite the channel or if the channel is already correct (prevent wrapping a wrapped channel)
|
||||
Object currentChannel = this.channelField.get(this.networkManager);
|
||||
if (currentChannel instanceof NettyChannelProxy) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the field is not correct, rewrite now to our handler
|
||||
Channel ch = new NettyChannelProxy(this.wrappedChannel, new NettyEventLoopProxy(this.wrappedChannel.eventLoop(), this) {
|
||||
@Override
|
||||
protected Runnable doProxyRunnable(Runnable original) {
|
||||
return NettyChannelInjector.this.processOutbound(original);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Callable<T> doProxyCallable(Callable<T> original) {
|
||||
return NettyChannelInjector.this.processOutbound(original);
|
||||
}
|
||||
}, this);
|
||||
this.channelField.set(this.networkManager, ch);
|
||||
}
|
||||
|
||||
private void ensureInEventLoop(Runnable runnable) {
|
||||
this.ensureInEventLoop(this.wrappedChannel.eventLoop(), runnable);
|
||||
}
|
||||
|
||||
private void ensureInEventLoop(EventLoop eventLoop, Runnable runnable) {
|
||||
if (eventLoop.inEventLoop()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
eventLoop.execute(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
void processInboundPacket(ChannelHandlerContext ctx, Object packet, Class<?> packetClass) {
|
||||
if (this.channelListener.hasMainThreadListener(packetClass) && !this.server.isPrimaryThread()) {
|
||||
// not on the main thread but we are required to be - re-schedule the packet on the main thread
|
||||
this.server.getScheduler().runTask(
|
||||
this.injectionFactory.getPlugin(),
|
||||
() -> this.processInboundPacket(ctx, packet, packetClass));
|
||||
return;
|
||||
}
|
||||
|
||||
// call packet handlers, a null result indicates that we shouldn't change anything
|
||||
PacketEvent interceptionResult = this.channelListener.onPacketReceiving(this, packet, null);
|
||||
if (interceptionResult == null) {
|
||||
this.ensureInEventLoop(ctx.channel().eventLoop(), () -> ctx.fireChannelRead(packet));
|
||||
return;
|
||||
}
|
||||
|
||||
// fire the intercepted packet down the pipeline if it wasn't cancelled
|
||||
if (!interceptionResult.isCancelled()) {
|
||||
this.ensureInEventLoop(
|
||||
ctx.channel().eventLoop(),
|
||||
() -> ctx.fireChannelRead(interceptionResult.getPacket().getHandle()));
|
||||
|
||||
// check if there were any post events added the packet after we fired it down the pipeline
|
||||
// we use this way as we don't want to construct a new network manager accidentally
|
||||
NetworkMarker marker = NetworkMarker.getNetworkMarker(interceptionResult);
|
||||
if (marker != null) {
|
||||
this.networkProcessor.invokePostEvent(interceptionResult, marker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<T> T processOutbound(T action) {
|
||||
// get the accessor to the packet field
|
||||
// if we are unable to look up the accessor then just return the runnable, probably nothing of our business
|
||||
FieldAccessor packetAccessor = this.lookupPacketAccessor(action);
|
||||
if (packetAccessor == NO_OP_ACCESSOR) {
|
||||
return action;
|
||||
}
|
||||
|
||||
// get the packet field and ensure that the field is actually present (should always be, just to be sure)
|
||||
Object packet = packetAccessor.get(action);
|
||||
if (packet == null) {
|
||||
return action;
|
||||
}
|
||||
|
||||
// filter out all packets which were explicitly send to not be processed by any event
|
||||
// pre-checking isEmpty will reduce the need of hashing packets which don't override the
|
||||
// hashCode method; this presents calls to the very slow identityHashCode default implementation
|
||||
NetworkMarker marker = this.savedMarkers.isEmpty() ? null : this.savedMarkers.remove(packet);
|
||||
if (!this.skippedPackets.isEmpty() && this.skippedPackets.remove(packet)) {
|
||||
// if a marker was set there might be scheduled packets to execute after the packet send
|
||||
// for this to work we need to proxy the input action to provide access to them
|
||||
if (marker != null) {
|
||||
return this.proxyAction(action, null, marker);
|
||||
}
|
||||
|
||||
// nothing special, just no processing
|
||||
return action;
|
||||
}
|
||||
|
||||
// no listener and no marker - no magic :)
|
||||
if (!this.channelListener.hasListener(packet.getClass()) && marker == null) {
|
||||
return action;
|
||||
}
|
||||
|
||||
// ensure that we are on the main thread if we need to
|
||||
if (this.channelListener.hasMainThreadListener(packet.getClass()) && !this.server.isPrimaryThread()) {
|
||||
// not on the main thread but we are required to be - re-schedule the packet on the main thread
|
||||
this.server.getScheduler().scheduleSyncDelayedTask(
|
||||
this.injectionFactory.getPlugin(),
|
||||
() -> this.sendServerPacket(packet, null, true));
|
||||
return null;
|
||||
}
|
||||
|
||||
// call all listeners which are listening to the outbound packet, if any
|
||||
// null indicates that no listener was affected by the packet, meaning that we can directly send the original packet
|
||||
PacketEvent event = this.channelListener.onPacketSending(this, packet, marker);
|
||||
if (event == null) {
|
||||
return action;
|
||||
}
|
||||
|
||||
// if the event wasn't cancelled by this action we must recheck if the packet changed during the method call
|
||||
if (!event.isCancelled()) {
|
||||
// rewrite the packet in the given action if the packet was changed during the event call
|
||||
Object interceptedPacket = event.getPacket().getHandle();
|
||||
if (interceptedPacket != packet) {
|
||||
packetAccessor.set(action, interceptedPacket);
|
||||
}
|
||||
|
||||
// this is essential to do this way as a call to getMarker on the event will construct a new marker instance if needed
|
||||
// we just want to know here if there is a marker to proceed correctly
|
||||
// if the marker is null we can just schedule the action as we don't need to do anything after the packet was sent
|
||||
NetworkMarker eventMarker = NetworkMarker.getNetworkMarker(event);
|
||||
if (eventMarker == null) {
|
||||
return action;
|
||||
}
|
||||
|
||||
// we need to wrap the action to call the listeners set in the marker
|
||||
return this.proxyAction(action, event, eventMarker);
|
||||
}
|
||||
|
||||
// return null if the event was cancelled to schedule a no-op event
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T proxyAction(T action, PacketEvent event, NetworkMarker marker) {
|
||||
// hack - we only know that the given action is either a runnable or callable, but we need to work out which thing
|
||||
// it is exactly to proceed correctly here.
|
||||
if (action instanceof Runnable) {
|
||||
// easier thing to do - just wrap the runnable in a new one
|
||||
return (T) (Runnable) () -> {
|
||||
// notify the outbound handler that the packets are processed
|
||||
this.processedPackets.set(Boolean.TRUE);
|
||||
// execute the action & invoke the post event
|
||||
((Runnable) action).run();
|
||||
this.networkProcessor.invokePostEvent(event, marker);
|
||||
};
|
||||
} else if (action instanceof Callable<?>) {
|
||||
// okay this is a bit harder now - we need to wrap the action and return the value of it
|
||||
return (T) (Callable<Object>) () -> {
|
||||
// notify the outbound handler that the packets are processed
|
||||
this.processedPackets.set(Boolean.TRUE);
|
||||
// execute the action & invoke the post event
|
||||
Object value = ((Callable<Object>) action).call();
|
||||
this.networkProcessor.invokePostEvent(event, marker);
|
||||
return value;
|
||||
};
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected input action of type " + action.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private FieldAccessor lookupPacketAccessor(Object action) {
|
||||
return PACKET_ACCESSORS.computeIfAbsent(action.getClass(), clazz -> {
|
||||
try {
|
||||
// find the field
|
||||
Field packetField = FuzzyReflection.fromClass(clazz, true).getField(FuzzyFieldContract
|
||||
.newBuilder()
|
||||
.typeSuperOf(MinecraftReflection.getPacketClass())
|
||||
.build());
|
||||
return Accessors.getFieldAccessor(packetField);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
// no such field found :(
|
||||
return NO_OP_ACCESSOR;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Object getPlayerConnection() {
|
||||
// resolve the player connection if needed
|
||||
if (this.playerConnection == null) {
|
||||
Player target = this.getPlayer();
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.playerConnection = MinecraftFields.getPlayerConnection(target);
|
||||
}
|
||||
|
||||
// cannot be null at this point
|
||||
return this.playerConnection;
|
||||
}
|
||||
|
||||
public Channel getWrappedChannel() {
|
||||
return this.wrappedChannel;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.injector.temporary.MinimalInjector;
|
||||
import java.net.SocketAddress;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
final class NettyChannelMinimalInjector implements MinimalInjector {
|
||||
|
||||
private final NettyChannelInjector injector;
|
||||
|
||||
public NettyChannelMinimalInjector(NettyChannelInjector injector) {
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getAddress() {
|
||||
return this.injector.getWrappedChannel().remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(String message) {
|
||||
this.injector.disconnect(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
this.injector.sendServerPacket(packet, marker, filtered);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return this.injector.getPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return this.injector.getWrappedChannel().isActive();
|
||||
}
|
||||
|
||||
public NettyChannelInjector getInjector() {
|
||||
return this.injector;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelMetadata;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelProgressivePromise;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.util.Attribute;
|
||||
import io.netty.util.AttributeKey;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.function.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A netty channel which has all methods delegated to another given channel except for the event loop which is proxied
|
||||
* separately. This class can not extend from AbstractChannel as the {@code newUnsafe()} method is protected and can
|
||||
* therefore not be called in the delegate channel.
|
||||
*/
|
||||
final class NettyChannelProxy implements Channel {
|
||||
|
||||
private final Channel delegate;
|
||||
private final EventLoop eventLoop;
|
||||
private final NettyChannelInjector injector;
|
||||
|
||||
public NettyChannelProxy(Channel delegate, EventLoop eventLoop, NettyChannelInjector injector) {
|
||||
this.delegate = delegate;
|
||||
this.eventLoop = eventLoop;
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventLoop eventLoop() {
|
||||
return this.eventLoop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel parent() {
|
||||
return this.delegate.parent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelConfig config() {
|
||||
return this.delegate.config();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return this.delegate.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered() {
|
||||
return this.delegate.isRegistered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return this.delegate.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelMetadata metadata() {
|
||||
return this.delegate.metadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress localAddress() {
|
||||
return this.delegate.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress remoteAddress() {
|
||||
return this.delegate.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture closeFuture() {
|
||||
return this.delegate.closeFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable() {
|
||||
return this.delegate.isWritable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unsafe unsafe() {
|
||||
return this.delegate.unsafe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline pipeline() {
|
||||
return this.delegate.pipeline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBufAllocator alloc() {
|
||||
return this.delegate.alloc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise newPromise() {
|
||||
return this.delegate.newPromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelProgressivePromise newProgressivePromise() {
|
||||
return this.delegate.newProgressivePromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture newSucceededFuture() {
|
||||
return this.delegate.newSucceededFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture newFailedFuture(Throwable cause) {
|
||||
return this.delegate.newFailedFuture(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise voidPromise() {
|
||||
return this.delegate.voidPromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture bind(SocketAddress localAddress) {
|
||||
return this.delegate.bind(localAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress remoteAddress) {
|
||||
return this.delegate.connect(remoteAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
||||
return this.delegate.connect(remoteAddress, localAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture disconnect() {
|
||||
return this.delegate.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture close() {
|
||||
return this.delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture deregister() {
|
||||
return this.delegate.deregister();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
|
||||
return this.delegate.bind(localAddress, promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
|
||||
return this.delegate.connect(remoteAddress, promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress,
|
||||
ChannelPromise promise) {
|
||||
return this.delegate.connect(remoteAddress, localAddress, promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture disconnect(ChannelPromise promise) {
|
||||
return this.delegate.disconnect(promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture close(ChannelPromise promise) {
|
||||
return this.delegate.close(promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture deregister(ChannelPromise promise) {
|
||||
return this.delegate.deregister(promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel read() {
|
||||
return this.delegate.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture write(Object msg) {
|
||||
return this.write(msg, this.newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture write(Object msg, ChannelPromise promise) {
|
||||
// only need to do our special handling if we are in the event loop
|
||||
if (this.isPacketEventCallNeeded(msg)) {
|
||||
this.processPacketOutbound(msg, packet -> this.delegate.write(packet, promise));
|
||||
return promise;
|
||||
} else {
|
||||
// no special handling needed, just write the packet
|
||||
return this.delegate.write(msg, promise);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel flush() {
|
||||
return this.delegate.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
|
||||
// only need to do our special handling if we are in the event loop
|
||||
if (this.isPacketEventCallNeeded(msg)) {
|
||||
this.processPacketOutbound(msg, packet -> this.delegate.writeAndFlush(packet, promise));
|
||||
return promise;
|
||||
} else {
|
||||
// no special handling needed, just write the packet
|
||||
return this.delegate.writeAndFlush(msg, promise);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object msg) {
|
||||
return this.writeAndFlush(msg, this.newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Attribute<T> attr(AttributeKey<T> key) {
|
||||
return this.delegate.attr(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Channel o) {
|
||||
return this.delegate.compareTo(o);
|
||||
}
|
||||
|
||||
private boolean isPacketEventCallNeeded(Object msg) {
|
||||
if (MinecraftReflection.isPacketClass(msg)) {
|
||||
// check if any packet was marked as processed before during the current execution
|
||||
// then reset the thread local as there will always be only one packet per write op (if needed)
|
||||
Boolean hasProcessedPacket = this.injector.processedPackets.get();
|
||||
if (hasProcessedPacket == Boolean.TRUE) {
|
||||
// at least one packet was processed before, so no need for us to process as well
|
||||
this.injector.processedPackets.set(Boolean.FALSE);
|
||||
return false;
|
||||
} else {
|
||||
// no packet was processed in the current context, we need to process
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// not a packet, just ignore
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void processPacketOutbound(Object packet, Consumer<Object> delegateActionHandler) {
|
||||
Runnable action = this.injector.processOutbound(() -> delegateActionHandler.accept(packet));
|
||||
if (action != null) {
|
||||
action.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 294 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user