Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc5caf143d | ||
|
|
35e37121f9 | ||
|
|
86130b5743 | ||
|
|
d5231944b6 | ||
|
|
f042087356 | ||
|
|
d78e00f80f | ||
|
|
421db9f02a | ||
|
|
51a84bbcd1 | ||
|
|
e1715e26b3 | ||
|
|
1e69a116ba | ||
|
|
04c41dc961 | ||
|
|
f1cb43bbc5 |
No files matched your search
@@ -1 +0,0 @@
|
||||
* text=auto eol=lf
|
||||
@@ -1,38 +0,0 @@
|
||||
Contributing to ProtocolLib
|
||||
===========================
|
||||
This page will detail specific things that must be done if you intend to contribute to this project.
|
||||
|
||||
## Updating ProtocolLib to a New Protocol Version
|
||||
#### Before we get started:
|
||||
1. **When do we need to update the protocol version?**
|
||||
_A Minecraft protocol version is a number for each specification of the Minecraft protocol.
|
||||
This means it is incremented when there is a change to the [Protocol Specification](http://wiki.vg/Protocol),
|
||||
but not necessarily for each Minecraft version update. If the latest Minecraft version contains a protocol change,
|
||||
that means ProtocolLib needs to be updated._
|
||||
2. **When do we need to update other parts of ProtocolLib?**
|
||||
_When the package version guard changes. The package version guard is the version string inserted
|
||||
into the package identifier for CraftBukkit and net.minecraft.server (e.g. v1_9_R2). It is incremented
|
||||
by Spigot if there is a major breaking change in the internal server code. This is to encourage plugins
|
||||
that interact directly with CraftBukkit/nms code to update more specifically. However, since most of
|
||||
ProtocolLib uses reflection, and the structure of packet classes is generally static, one will have
|
||||
to go through the code and change imports from nms/CraftBukkit._
|
||||
|
||||
#### Ready? Let's get started!
|
||||
1. Read the [Protocol Changes](http://wiki.vg/Protocol_History). Always make sure the list is both
|
||||
complete and correct. If you're unsure, don't hesitate to ask in #mcdevs (the people who maintain wiki.vg) on [libera.chat](http://libera.chat)
|
||||
or #spigot on [irc.spi.gt](http://irc.spi.gt) ([webchat](https://irc.spi.gt/iris/?channels=spigot)).
|
||||
2. Search for usages of the now-defunct NMS package guard and change them.
|
||||
3. The class `com.comphenix.protocol.PacketType` contains a list of all the packets. If any packets were added or removed
|
||||
(or had their ID changed), make sure to update this list. If a packet was removed
|
||||
in favor of usage of another packet, instead of removing it, move it to the bottom of the list
|
||||
in its section, add a deprecation warning to it, and redirect it to the packet that replaced it.
|
||||
4. `mvn` in the root directory to build the project.
|
||||
5. If tests fail in the maven build, go through the tests to make sure you removed references to any removed packets
|
||||
and changed the NMS version guards.
|
||||
6. Increment the package version in `com.comphenix.protocol.utility.Constants`.
|
||||
7. `com.comphenix.protocol.ProtocolLibrary` contains several constants that must be updated, including the Minecraft version
|
||||
and the release date.
|
||||
8. `com.comphenix.protocol.utility.MinecraftProtocolVersion` contains a map of all the protocol version integers.
|
||||
If the protocol version has been incremented, add a new line to the map.
|
||||
9. `mvn` in root directory again. If it builds successfully, test on the appropriate version of a Spigot server. If
|
||||
the build fails, debug!
|
||||
@@ -1,2 +0,0 @@
|
||||
github: [dmulloy2]
|
||||
custom: ["https://paypal.me/dmulloy2"]
|
||||
@@ -1,12 +0,0 @@
|
||||
_Follow this template except for feature requests. Use pastebin when providing /protocol dump and any relevant errors._
|
||||
|
||||
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`
|
||||
|
||||
Debug paste link:
|
||||
|
||||
Description and relevant errors:
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
name: API question
|
||||
about: Questions related to using ProtocolLib in your plugins
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Make sure you're doing the following**
|
||||
- [ ] You're using the latest build for your server version
|
||||
- [ ] This isn't an issue caused by another plugin
|
||||
- [ ] You've checked for duplicate issues
|
||||
- [ ] You didn't use `/reload`
|
||||
|
||||
**Describe the question**
|
||||
A clear and concise description of what your question is.
|
||||
|
||||
**API method(s) used**
|
||||
List what API method(s) you're using
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Code**
|
||||
If applicable, add relevant code from your project
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
- [ ] This issue is not solved in a development build
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Version Info**
|
||||
Provide your ProtocolLib install info with `/protocol dump` through pastebin.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
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 [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -1,28 +0,0 @@
|
||||
name: ProtocolLib full build lifecycle
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '19'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Run gradle build lifecycle
|
||||
run: ./gradlew build shadowJar --no-daemon
|
||||
|
||||
- name: Upload plugin file
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ProtocolLib
|
||||
path: build/libs/ProtocolLib.jar
|
||||
@@ -1,41 +0,0 @@
|
||||
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: '19'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: 'java'
|
||||
|
||||
- name: Run gradle build lifecycle
|
||||
run: ./gradlew build -x test --no-daemon
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
+8
-26
@@ -2,25 +2,21 @@
|
||||
## Eclipse
|
||||
#################
|
||||
|
||||
# Generic project files
|
||||
.project
|
||||
.classpath
|
||||
.settings/
|
||||
bin/
|
||||
.idea/
|
||||
*.iml
|
||||
.ipr
|
||||
.iws
|
||||
|
||||
*.pydevproject
|
||||
.project
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
target/
|
||||
.gitignore
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
@@ -34,12 +30,6 @@ local.properties
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
#################
|
||||
## Maven
|
||||
#################
|
||||
|
||||
target/
|
||||
dependency-reduced-pom.xml
|
||||
|
||||
#################
|
||||
## Visual Studio
|
||||
@@ -165,19 +155,11 @@ pip-log.txt
|
||||
.coverage
|
||||
.tox
|
||||
|
||||
# Translations
|
||||
#Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
|
||||
# Log4J files
|
||||
logs/
|
||||
|
||||
.gradle/
|
||||
build/
|
||||
logs/
|
||||
.java-version
|
||||
@@ -0,0 +1,165 @@
|
||||
#################
|
||||
## Eclipse
|
||||
#################
|
||||
|
||||
*.pydevproject
|
||||
.project
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
target/
|
||||
.gitignore
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
|
||||
#################
|
||||
## Visual Studio
|
||||
#################
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.vspscc
|
||||
.builds
|
||||
*.dotCover
|
||||
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment this
|
||||
#packages/
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish
|
||||
|
||||
# Others
|
||||
[Bb]in
|
||||
[Oo]bj
|
||||
sql
|
||||
TestResults
|
||||
*.Cache
|
||||
ClientBin
|
||||
stylecop.*
|
||||
~$*
|
||||
*.dbmdl
|
||||
Generated_Code #added for RIA/Silverlight projects
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
|
||||
|
||||
|
||||
############
|
||||
## Windows
|
||||
############
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
|
||||
#############
|
||||
## Python
|
||||
#############
|
||||
|
||||
*.py[co]
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
|
||||
#Translations
|
||||
*.mo
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
@@ -0,0 +1,72 @@
|
||||
<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.itemdisguise</groupId>
|
||||
<artifactId>ItemDisguise</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<name>Item Disguise</name>
|
||||
<description>Change the appearance of inventory items.</description>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>cp1252</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>bukkit-rep</id>
|
||||
<url>http://repo.bukkit.org/content/groups/public</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>comphenix-rep</id>
|
||||
<name>Comphenix Maven Releases</name>
|
||||
<url>http://repo.comphenix.net/content/groups/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/java</directory>
|
||||
<excludes>
|
||||
<exclude>**/*.java</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/aadnk/ProtocolLib.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:aadnk/ProtocolLib.git</developerConnection>
|
||||
<url>https://github.com/aadnk/ProtocolLib</url>
|
||||
</scm>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GNU GENERAL PUBLIC LICENSE - Version 2, June 1991</name>
|
||||
<url>http://www.gnu.org/licenses/gpl-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>1.3.2-R1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* ItemDisguise - A simple Bukkit plugin that illustrates how to use ProtocolLib.
|
||||
* 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.itemdisguise;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
|
||||
public class HideEnchantmentsListener {
|
||||
|
||||
private final Server server;
|
||||
private final Logger logger;
|
||||
|
||||
public HideEnchantmentsListener(Server server, Logger logger) {
|
||||
this.server = server;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public void addListener(ProtocolManager protocolManager, JavaPlugin plugin) {
|
||||
|
||||
// Hide all enchantments
|
||||
protocolManager.addPacketListener(new PacketAdapter(plugin, ConnectionSide.SERVER_SIDE, 0x67, 0x68, 0x3E) {
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
PacketContainer packet = event.getPacket();
|
||||
|
||||
try {
|
||||
// Item packets
|
||||
switch (event.getPacketID()) {
|
||||
case 0x67: // Set slot
|
||||
removeEnchantments(packet.getItemModifier().read(0));
|
||||
break;
|
||||
|
||||
case 0x68: // Set Window Items
|
||||
ItemStack[] elements = packet.getItemArrayModifier().read(0);
|
||||
|
||||
for (int i = 0; i < elements.length; i++) {
|
||||
if (elements[i] != null) {
|
||||
removeEnchantments(elements[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x3E: // Sound effect
|
||||
event.setCancelled(true);
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (FieldAccessException e) {
|
||||
logger.log(Level.SEVERE, "Couldn't access field.", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Censor
|
||||
protocolManager.addPacketListener(new PacketAdapter(plugin, ConnectionSide.CLIENT_SIDE, 0x3) {
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
if (event.getPacketID() == 0x3) {
|
||||
try {
|
||||
String message = event.getPacket().
|
||||
getSpecificModifier(String.class).read(0);
|
||||
|
||||
if (message.contains("shit") || message.contains("fuck")) {
|
||||
event.setCancelled(true);
|
||||
event.getPlayer().sendMessage("Bad manners!");
|
||||
}
|
||||
|
||||
} catch (FieldAccessException e) {
|
||||
logger.log(Level.SEVERE, "Couldn't access field.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Server getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public void removeListener(ProtocolManager protocolManager, JavaPlugin plugin) {
|
||||
// Just remove every adapter with this plugin
|
||||
protocolManager.removePacketListeners(plugin);
|
||||
}
|
||||
|
||||
private void removeEnchantments(ItemStack stack) {
|
||||
if (stack == null)
|
||||
return;
|
||||
|
||||
Object[] copy = stack.getEnchantments().keySet().toArray();
|
||||
|
||||
for (Object enchantment : copy) {
|
||||
stack.removeEnchantment((Enchantment) enchantment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* ItemDisguise - A simple Bukkit plugin that illustrates how to use ProtocolLib.
|
||||
* 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.itemdisguise;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
|
||||
public class ItemDisguiseMod extends JavaPlugin {
|
||||
|
||||
private ProtocolManager protocolManager;
|
||||
private Logger logger;
|
||||
|
||||
private HideEnchantmentsListener enchantmentsListener;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
|
||||
logger = getLoggerSafely();
|
||||
protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
enchantmentsListener = new HideEnchantmentsListener(getServer(), logger);
|
||||
enchantmentsListener.addListener(protocolManager, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
enchantmentsListener.removeListener(protocolManager, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (sender instanceof Player) {
|
||||
|
||||
Player player = (Player) sender;
|
||||
|
||||
if (label.equalsIgnoreCase("explosion")) {
|
||||
PacketContainer fakeExplosion = protocolManager.createPacket(0x3C);
|
||||
|
||||
// Set the coordinates
|
||||
try {
|
||||
fakeExplosion.getSpecificModifier(double.class).
|
||||
write(0, player.getLocation().getX()).
|
||||
write(1, player.getLocation().getY()).
|
||||
write(2, player.getLocation().getZ());
|
||||
fakeExplosion.getSpecificModifier(float.class).
|
||||
write(0, 3.0F);
|
||||
|
||||
protocolManager.sendServerPacket(player, fakeExplosion);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the Bukkit logger first, before we try to create our own
|
||||
private Logger getLoggerSafely() {
|
||||
|
||||
Logger log = null;
|
||||
|
||||
try {
|
||||
log = getLogger();
|
||||
} catch (Throwable e) {
|
||||
// We'll handle it
|
||||
}
|
||||
|
||||
if (log == null)
|
||||
log = Logger.getLogger("Minecraft");
|
||||
return log;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
name: ItemDisguiseMod
|
||||
version: 1.0.0
|
||||
description: Change the appearance of inventory items.
|
||||
author: Comphenix
|
||||
website: http://www.comphenix.net/ItemDisguise
|
||||
|
||||
main: com.comphenix.itemdisguise.ItemDisguiseMod
|
||||
depends: [ProtocolLib]
|
||||
database: false
|
||||
|
||||
commands:
|
||||
explosion:
|
||||
description: Creates a fake explosion around the caller.
|
||||
usage: /<command>
|
||||
@@ -0,0 +1,165 @@
|
||||
#################
|
||||
## Eclipse
|
||||
#################
|
||||
|
||||
*.pydevproject
|
||||
.project
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
target/
|
||||
.gitignore
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
|
||||
#################
|
||||
## Visual Studio
|
||||
#################
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.vspscc
|
||||
.builds
|
||||
*.dotCover
|
||||
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment this
|
||||
#packages/
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish
|
||||
|
||||
# Others
|
||||
[Bb]in
|
||||
[Oo]bj
|
||||
sql
|
||||
TestResults
|
||||
*.Cache
|
||||
ClientBin
|
||||
stylecop.*
|
||||
~$*
|
||||
*.dbmdl
|
||||
Generated_Code #added for RIA/Silverlight projects
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
|
||||
|
||||
|
||||
############
|
||||
## Windows
|
||||
############
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
|
||||
#############
|
||||
## Python
|
||||
#############
|
||||
|
||||
*.py[co]
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
|
||||
#Translations
|
||||
*.mo
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
@@ -3,29 +3,32 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.comphenix.tinyprotocol</groupId>
|
||||
<artifactId>TinyProtocol</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<version>0.1.0</version>
|
||||
<name>TinyProtocol</name>
|
||||
<description>Intercept packets without ProtocolLib.</description>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib-Parent</artifactId>
|
||||
<version>v4-SNAPSHOT</version>
|
||||
<relativePath>../../</relativePath>
|
||||
</parent>
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>comphenix-releases</id>
|
||||
<name>Comphenix Maven Releases</name>
|
||||
<url>http://repo.comphenix.net/content/repositories/releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>comphenix-snapshots</id>
|
||||
<name>Comphenix Maven Snapshots</name>
|
||||
<url>http://repo.comphenix.net/content/repositories/snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
||||
<id>md_5-repo</id>
|
||||
<url>http://repo.md-5.net/content/repositories/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||
<id>comphenix-rep</id>
|
||||
<name>Comphenix Maven Releases</name>
|
||||
<url>http://repo.comphenix.net/content/groups/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
@@ -42,41 +45,26 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.0</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.5</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<addMavenDescriptor>false</addMavenDescriptor>
|
||||
</archive>
|
||||
<finalName>TinyProtocol</finalName>
|
||||
<outputDirectory>../../target/</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<id>attach-javadocs</id>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.7.10-R0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.7.10-R0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release-sign-artifacts</id>
|
||||
+15
-23
@@ -1,7 +1,5 @@
|
||||
package com.comphenix.tinyprotocol;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.Command;
|
||||
@@ -12,13 +10,13 @@ import org.bukkit.plugin.java.JavaPlugin;
|
||||
import com.comphenix.tinyprotocol.Reflection.ConstructorInvoker;
|
||||
import com.comphenix.tinyprotocol.Reflection.FieldAccessor;
|
||||
|
||||
/**
|
||||
* Represents an example plugin utilizing TinyProtocol
|
||||
*/
|
||||
import net.minecraft.util.io.netty.channel.Channel;
|
||||
|
||||
public class ExamplePlugin extends JavaPlugin {
|
||||
// Chat packets
|
||||
private FieldAccessor<String> CHAT_MESSAGE = Reflection.getField("{nms}.PacketPlayInChat", String.class, 0);
|
||||
|
||||
private FieldAccessor<String> CHAT_MESSAGE = Reflection.getField(
|
||||
"{nms}.PacketPlayInChat", String.class, 0);
|
||||
|
||||
// Explosion packet
|
||||
private Class<?> particleClass = Reflection.getClass("{nms}.PacketPlayOutWorldParticles");
|
||||
private FieldAccessor<String> particleName = Reflection.getField(particleClass, String.class, 0);
|
||||
@@ -26,7 +24,7 @@ public class ExamplePlugin extends JavaPlugin {
|
||||
private FieldAccessor<Float> particleY = Reflection.getField(particleClass, float.class, 1);
|
||||
private FieldAccessor<Float> particleZ = Reflection.getField(particleClass, float.class, 2);
|
||||
private FieldAccessor<Integer> particleCount = Reflection.getField(particleClass, int.class, 0);
|
||||
|
||||
|
||||
// Server info packet
|
||||
private Class<?> serverInfoClass = Reflection.getClass("{nms}.PacketStatusOutServerInfo");
|
||||
private Class<Object> serverPingClass = Reflection.getUntypedClass("{nms}.ServerPing");
|
||||
@@ -34,13 +32,12 @@ public class ExamplePlugin extends JavaPlugin {
|
||||
private FieldAccessor<Object> serverPing = Reflection.getField(serverInfoClass, serverPingClass, 0);
|
||||
private FieldAccessor<Object> playerSample = Reflection.getField(serverPingClass, playerSampleClass, 0);
|
||||
private ConstructorInvoker playerSampleInvoker = Reflection.getConstructor(playerSampleClass, int.class, int.class);
|
||||
|
||||
|
||||
private TinyProtocol protocol;
|
||||
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
protocol = new TinyProtocol(this) {
|
||||
|
||||
@Override
|
||||
public Object onPacketInAsync(Player sender, Channel channel, Object packet) {
|
||||
// Cancel chat packets
|
||||
@@ -50,28 +47,24 @@ public class ExamplePlugin extends JavaPlugin {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (particleName.hasField(packet)) {
|
||||
System.out.println("Sending particle field:" + packet);
|
||||
}
|
||||
|
||||
return super.onPacketInAsync(sender, channel, packet);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object onPacketOutAsync(Player reciever, Channel channel, Object packet) {
|
||||
if (serverInfoClass.isInstance(packet)) {
|
||||
Object ping = serverPing.get(packet);
|
||||
playerSample.set(ping, playerSampleInvoker.invoke(1000, 0));
|
||||
|
||||
|
||||
// Which is equivalent to:
|
||||
// serverPing.get(packet).setPlayerSample(new ServerPingPlayerSample(1000, 0));
|
||||
//serverPing.get(packet).setPlayerSample(new ServerPingPlayerSample(1000, 0));
|
||||
return packet;
|
||||
}
|
||||
|
||||
return super.onPacketOutAsync(reciever, channel, packet);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -79,7 +72,7 @@ public class ExamplePlugin extends JavaPlugin {
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (sender instanceof Player) {
|
||||
Player player = (Player) sender;
|
||||
|
||||
|
||||
// Toggle injection
|
||||
if (protocol.hasInjected(player)) {
|
||||
protocol.uninjectPlayer(player);
|
||||
@@ -88,15 +81,13 @@ public class ExamplePlugin extends JavaPlugin {
|
||||
protocol.injectPlayer(player);
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "Player " + player + " has been injected.");
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "Can only be invoked by a player.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void sendExplosion(Player player) {
|
||||
try {
|
||||
// Only visible for the client
|
||||
@@ -110,6 +101,7 @@ public class ExamplePlugin extends JavaPlugin {
|
||||
|
||||
// Send the packet to the player
|
||||
protocol.sendPacket(player, explosionPacket);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot send packet.", e);
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
package com.comphenix.tinyprotocol;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
/**
|
||||
* An utility class that simplifies reflection in Bukkit plugins.
|
||||
* @author Kristian
|
||||
*/
|
||||
public final class Reflection {
|
||||
/**
|
||||
* An interface for invoking a specific constructor.
|
||||
*/
|
||||
public interface ConstructorInvoker {
|
||||
/**
|
||||
* Invoke a constructor for a specific class.
|
||||
* @param arguments - the arguments to pass to the constructor.
|
||||
* @return The constructed object.
|
||||
*/
|
||||
public Object invoke(Object... arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for invoking a specific method.
|
||||
*/
|
||||
public interface MethodInvoker {
|
||||
/**
|
||||
* Invoke a method on a specific target object.
|
||||
* @param target - the target object, or NULL for a static method.
|
||||
* @param arguments - the arguments to pass to the method.
|
||||
* @return The return value, or NULL if is void.
|
||||
*/
|
||||
public Object invoke(Object target, Object... arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for retrieving the field content.
|
||||
* @param <T> - field type.
|
||||
*/
|
||||
public interface FieldAccessor<T> {
|
||||
/**
|
||||
* Retrieve the content of a field.
|
||||
* @param target - the target object, or NULL for a static field.
|
||||
* @return The value of the field.
|
||||
*/
|
||||
public T get(Object target);
|
||||
|
||||
/**
|
||||
* Set the content of a field.
|
||||
* @param target - the target object, or NULL for a static field.
|
||||
* @param value - the new value of the field.
|
||||
*/
|
||||
public void set(Object target, Object value);
|
||||
|
||||
/**
|
||||
* Determine if the given object has this field.
|
||||
* @param target - the object to test.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasField(Object target);
|
||||
}
|
||||
|
||||
// Deduce the net.minecraft.server.v* package
|
||||
private static String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName();
|
||||
private static String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
private static String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", "");
|
||||
|
||||
// Variable replacement
|
||||
private static Pattern MATCH_VARIABLE = Pattern.compile("\\{([^\\}]+)\\}");
|
||||
|
||||
private Reflection() {
|
||||
// Seal class
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field accessor for a specific field type and name.
|
||||
* @param target - the target type.
|
||||
* @param name - the name of the field, or NULL to ignore.
|
||||
* @param fieldType - a compatible field type.
|
||||
* @return The field accessor.
|
||||
*/
|
||||
public static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType) {
|
||||
return getField(target, name, fieldType, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field accessor for a specific field type and name.
|
||||
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||
* @param name - the name of the field, or NULL to ignore.
|
||||
* @param fieldType - a compatible field type.
|
||||
* @return The field accessor.
|
||||
*/
|
||||
public static <T> FieldAccessor<T> getField(String className, String name, Class<T> fieldType) {
|
||||
return getField(getClass(className), name, fieldType, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field accessor for a specific field type and name.
|
||||
* @param target - the target type.
|
||||
* @param fieldType - a compatible field type.
|
||||
* @param index - the number of compatible fields to skip.
|
||||
* @return The field accessor.
|
||||
*/
|
||||
public static <T> FieldAccessor<T> getField(Class<?> target, Class<T> fieldType, int index) {
|
||||
return getField(target, null, fieldType, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field accessor for a specific field type and name.
|
||||
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||
* @param fieldType - a compatible field type.
|
||||
* @param index - the number of compatible fields to skip.
|
||||
* @return The field accessor.
|
||||
*/
|
||||
public static <T> FieldAccessor<T> getField(String className, Class<T> fieldType, int index) {
|
||||
return getField(getClass(className), fieldType, index);
|
||||
}
|
||||
|
||||
// Common method
|
||||
private static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType, int index) {
|
||||
for (final Field field : target.getDeclaredFields()) {
|
||||
if ((name == null || field.getName().equals(name)) &&
|
||||
fieldType.isAssignableFrom(field.getType()) && index-- <= 0) {
|
||||
field.setAccessible(true);
|
||||
|
||||
// A function for retrieving a specific field value
|
||||
return new FieldAccessor<T>() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T get(Object target) {
|
||||
try {
|
||||
return (T) field.get(target);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot access reflection.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object target, Object value) {
|
||||
try {
|
||||
field.set(target, value);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot access reflection.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasField(Object target) {
|
||||
// target instanceof DeclaringClass
|
||||
return field.getDeclaringClass().isAssignableFrom(target.getClass());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Search in parent classes
|
||||
if (target.getSuperclass() != null)
|
||||
return getField(target.getSuperclass(), name, fieldType, index);
|
||||
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first publically and privately defined method of the given name and parameter count.
|
||||
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||
* @param methodName - the method name, or NULL to skip.
|
||||
* @param params - the expected parameters.
|
||||
* @return An object that invokes this specific method.
|
||||
* @throws IllegalStateException If we cannot find this method.
|
||||
*/
|
||||
public static MethodInvoker getMethod(String className, String methodName, Class<?>... params) {
|
||||
return getTypedMethod(getClass(className), methodName, null, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first publically and privately defined method of the given name and parameter count.
|
||||
* @param clazz - a class to start with.
|
||||
* @param methodName - the method name, or NULL to skip.
|
||||
* @param params - the expected parameters.
|
||||
* @return An object that invokes this specific method.
|
||||
* @throws IllegalStateException If we cannot find this method.
|
||||
*/
|
||||
public static MethodInvoker getMethod(Class<?> clazz, String methodName, Class<?>... params) {
|
||||
return getTypedMethod(clazz, methodName, null, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first publically and privately defined method of the given name and parameter count.
|
||||
* @param clazz - a class to start with.
|
||||
* @param methodName - the method name, or NULL to skip.
|
||||
* @param returnType - the expected return type, or NULL to ignore.
|
||||
* @param params - the expected parameters.
|
||||
* @return An object that invokes this specific method.
|
||||
* @throws IllegalStateException If we cannot find this method.
|
||||
*/
|
||||
public static MethodInvoker getTypedMethod(Class<?> clazz, String methodName, Class<?> returnType, Class<?>... params) {
|
||||
for (final Method method : clazz.getDeclaredMethods()) {
|
||||
if ((methodName == null || method.getName().equals(methodName)) &&
|
||||
(returnType == null) || method.getReturnType().equals(returnType) &&
|
||||
Arrays.equals(method.getParameterTypes(), params)) {
|
||||
|
||||
method.setAccessible(true);
|
||||
return new MethodInvoker() {
|
||||
@Override
|
||||
public Object invoke(Object target, Object... arguments) {
|
||||
try {
|
||||
return method.invoke(target, arguments);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot invoke method " + method, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
// Search in every superclass
|
||||
if (clazz.getSuperclass() != null)
|
||||
return getMethod(clazz.getSuperclass(), methodName, params);
|
||||
throw new IllegalStateException(String.format(
|
||||
"Unable to find method %s (%s).", methodName, Arrays.asList(params)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first publically and privately defined constructor of the given name and parameter count.
|
||||
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||
* @param params - the expected parameters.
|
||||
* @return An object that invokes this constructor.
|
||||
* @throws IllegalStateException If we cannot find this method.
|
||||
*/
|
||||
public static ConstructorInvoker getConstructor(String className, Class<?>... params) {
|
||||
return getConstructor(getClass(className), params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first publically and privately defined constructor of the given name and parameter count.
|
||||
* @param clazz - a class to start with.
|
||||
* @param params - the expected parameters.
|
||||
* @return An object that invokes this constructor.
|
||||
* @throws IllegalStateException If we cannot find this method.
|
||||
*/
|
||||
public static ConstructorInvoker getConstructor(Class<?> clazz, Class<?>... params) {
|
||||
for (final Constructor<?> constructor : clazz.getDeclaredConstructors()) {
|
||||
if (Arrays.equals(constructor.getParameterTypes(), params)) {
|
||||
|
||||
constructor.setAccessible(true);
|
||||
return new ConstructorInvoker() {
|
||||
@Override
|
||||
public Object invoke(Object... arguments) {
|
||||
try {
|
||||
return constructor.newInstance(arguments);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot invoke constructor " + constructor, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(String.format(
|
||||
"Unable to find constructor for %s (%s).", clazz, Arrays.asList(params)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class from its full name, without knowing its type on compile time.
|
||||
* <p>
|
||||
* This is useful when looking up fields by a NMS or OBC type.
|
||||
* <p>
|
||||
* @see {@link #getClass()} for more information.
|
||||
* @param lookupName - the class name with variables.
|
||||
* @return The class.
|
||||
*/
|
||||
public static Class<Object> getUntypedClass(String lookupName) {
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
Class<Object> clazz = (Class<Object>)(Class)getClass(lookupName);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class from its full name.
|
||||
* <p>
|
||||
* Strings enclosed with curly brackets - such as {TEXT} - will be replaced according
|
||||
* to the following table:
|
||||
* <p>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <th>Variable</th>
|
||||
* <th>Content</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{nms}</td>
|
||||
* <td>Actual package name of net.minecraft.server.VERSION</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{obc}</td>
|
||||
* <td>Actual pacakge name of org.bukkit.craftbukkit.VERSION</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{version}</td>
|
||||
* <td>The current Minecraft package VERSION, if any.</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* @param lookupName - the class name with variables.
|
||||
* @return The looked up class.
|
||||
* @throws IllegalArgumentException If a variable or class could not be found.
|
||||
*/
|
||||
public static Class<?> getClass(String lookupName) {
|
||||
return getCanonicalClass(expandVariables(lookupName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class in the net.minecraft.server.VERSION.* package.
|
||||
* @param name - the name of the class, excluding the package.
|
||||
* @throws IllegalArgumentException If the class doesn't exist.
|
||||
*/
|
||||
public static Class<?> getMinecraftClass(String name) {
|
||||
return getCanonicalClass(NMS_PREFIX + "." + name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class in the org.bukkit.craftbukkit.VERSION.* package.
|
||||
* @param name - the name of the class, excluding the package.
|
||||
* @throws IllegalArgumentException If the class doesn't exist.
|
||||
*/
|
||||
public static Class<?> getCraftBukkitClass(String name) {
|
||||
return getCanonicalClass(OBC_PREFIX + "." + name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class by its canonical name.
|
||||
* @param canonicalName - the canonical name.
|
||||
* @return The class.
|
||||
*/
|
||||
private static Class<?> getCanonicalClass(String canonicalName) {
|
||||
try {
|
||||
return Class.forName(canonicalName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException("Cannot find " + canonicalName, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand variables such as "{nms}" and "{obc}" to their corresponding packages.
|
||||
* @param name - the full name of the class.
|
||||
* @return The expanded string.
|
||||
*/
|
||||
private static String expandVariables(String name) {
|
||||
StringBuffer output = new StringBuffer();
|
||||
Matcher matcher = MATCH_VARIABLE.matcher(name);
|
||||
|
||||
while (matcher.find()) {
|
||||
String variable = matcher.group(1);
|
||||
String replacement = "";
|
||||
|
||||
// Expand all detected variables
|
||||
if ("nms".equalsIgnoreCase(variable))
|
||||
replacement = NMS_PREFIX;
|
||||
else if ("obc".equalsIgnoreCase(variable))
|
||||
replacement = OBC_PREFIX;
|
||||
else if ("version".equalsIgnoreCase(variable))
|
||||
replacement = VERSION;
|
||||
else
|
||||
throw new IllegalArgumentException("Unknown variable: " + variable);
|
||||
|
||||
// Assume the expanded variables are all packages, and append a dot
|
||||
if (replacement.length() > 0 && matcher.end() < name.length() && name.charAt(matcher.end()) != '.')
|
||||
replacement += ".";
|
||||
matcher.appendReplacement(output, Matcher.quoteReplacement(replacement));
|
||||
}
|
||||
matcher.appendTail(output);
|
||||
return output.toString();
|
||||
}
|
||||
}
|
||||
+129
-198
@@ -1,15 +1,5 @@
|
||||
package com.comphenix.tinyprotocol;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -18,6 +8,17 @@ import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import net.minecraft.util.com.mojang.authlib.GameProfile;
|
||||
// These are not versioned, but they require CraftBukkit
|
||||
import net.minecraft.util.io.netty.channel.Channel;
|
||||
import net.minecraft.util.io.netty.channel.ChannelDuplexHandler;
|
||||
import net.minecraft.util.io.netty.channel.ChannelFuture;
|
||||
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
||||
import net.minecraft.util.io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import net.minecraft.util.io.netty.channel.ChannelInitializer;
|
||||
import net.minecraft.util.io.netty.channel.ChannelPipeline;
|
||||
import net.minecraft.util.io.netty.channel.ChannelPromise;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -27,230 +28,182 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import com.comphenix.tinyprotocol.Reflection.FieldAccessor;
|
||||
import com.comphenix.tinyprotocol.Reflection.MethodInvoker;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
/**
|
||||
* Represents a very tiny alternative to ProtocolLib.
|
||||
* Represents a very tiny alternative to ProtocolLib in 1.7.2.
|
||||
* <p>
|
||||
* It now supports intercepting packets during login and status ping (such as OUT_SERVER_PING)!
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class TinyProtocol {
|
||||
private static final AtomicInteger ID = new AtomicInteger(0);
|
||||
|
||||
// Required Minecraft classes
|
||||
private static final Class<?> entityPlayerClass = Reflection.getClass("{nms}.EntityPlayer", "net.minecraft.server.level.EntityPlayer");
|
||||
private static final Class<?> playerConnectionClass = Reflection.getClass("{nms}.PlayerConnection", "net.minecraft.server.network.PlayerConnection");
|
||||
private static final Class<?> networkManagerClass = Reflection.getClass("{nms}.NetworkManager", "net.minecraft.network.NetworkManager");
|
||||
|
||||
|
||||
// Used in order to lookup a channel
|
||||
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
|
||||
private static final FieldAccessor<?> getConnection = Reflection.getField(entityPlayerClass, null, playerConnectionClass);
|
||||
private static final FieldAccessor<?> getManager = Reflection.getField(playerConnectionClass, null, networkManagerClass);
|
||||
private static final FieldAccessor<Channel> getChannel = Reflection.getField(networkManagerClass, Channel.class, 0);
|
||||
|
||||
private static final FieldAccessor<Object> getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class);
|
||||
private static final FieldAccessor<Object> getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class);
|
||||
private static final FieldAccessor<Channel> getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0);
|
||||
|
||||
// Looking up ServerConnection
|
||||
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer", "net.minecraft.server.MinecraftServer");
|
||||
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection", "net.minecraft.server.network.ServerConnection");
|
||||
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer");
|
||||
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection");
|
||||
private static final FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
|
||||
private static final FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
|
||||
|
||||
private static final MethodInvoker getNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
|
||||
|
||||
// Packets we have to intercept
|
||||
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getClass("{nms}.PacketLoginInStart", "net.minecraft.network.protocol.login.PacketLoginInStart");
|
||||
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart");
|
||||
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
|
||||
|
||||
|
||||
// Speedup channel lookup
|
||||
private Map<String, Channel> channelLookup = new MapMaker().weakValues().makeMap();
|
||||
private Listener listener;
|
||||
|
||||
|
||||
// Channels that have already been removed
|
||||
private Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().<Channel, Boolean>makeMap());
|
||||
|
||||
|
||||
// List of network markers
|
||||
private List<Object> networkManagers;
|
||||
|
||||
|
||||
// Injected channel handlers
|
||||
private List<Channel> serverChannels = new ArrayList<>();
|
||||
private List<Channel> serverChannels = Lists.newArrayList();
|
||||
private ChannelInboundHandlerAdapter serverChannelHandler;
|
||||
private ChannelInitializer<Channel> beginInitProtocol;
|
||||
private ChannelInitializer<Channel> endInitProtocol;
|
||||
|
||||
|
||||
// Current handler name
|
||||
private String handlerName;
|
||||
|
||||
|
||||
protected volatile boolean closed;
|
||||
protected Plugin plugin;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients.
|
||||
* Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients.
|
||||
* <p>
|
||||
* You can construct multiple instances per plugin.
|
||||
*
|
||||
* @param plugin - the plugin.
|
||||
*/
|
||||
public TinyProtocol(final Plugin plugin) {
|
||||
public TinyProtocol(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
|
||||
// Compute handler name
|
||||
this.handlerName = getHandlerName();
|
||||
|
||||
|
||||
// Prepare existing players
|
||||
registerBukkitEvents();
|
||||
|
||||
try {
|
||||
registerChannelHandler();
|
||||
registerPlayers(plugin);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Damn you, late bind
|
||||
plugin.getLogger().info("[TinyProtocol] Delaying server channel injection due to late bind.");
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
registerChannelHandler();
|
||||
registerPlayers(plugin);
|
||||
plugin.getLogger().info("[TinyProtocol] Late bind injection successful.");
|
||||
}
|
||||
}.runTask(plugin);
|
||||
}
|
||||
registerChannelHandler();
|
||||
registerPlayers(plugin);
|
||||
}
|
||||
|
||||
private void createServerChannelHandler() {
|
||||
// Handle connected channels
|
||||
endInitProtocol = new ChannelInitializer<Channel>() {
|
||||
|
||||
@Override
|
||||
protected void initChannel(Channel channel) throws Exception {
|
||||
try {
|
||||
// This can take a while, so we need to stop the main thread from interfering
|
||||
synchronized (networkManagers) {
|
||||
// Stop injecting channels
|
||||
if (!closed) {
|
||||
channel.eventLoop().submit(() -> injectChannelInternal(channel));
|
||||
}
|
||||
// Handle connected channels
|
||||
endInitProtocol = new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel channel) throws Exception {
|
||||
try {
|
||||
// This can take a while, so we need to stop the main thread from interfering
|
||||
synchronized (networkManagers) {
|
||||
// Stop injecting channels
|
||||
if (closed)
|
||||
return;
|
||||
injectChannelInternal(channel);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Cannot inject incomming channel " + channel, e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Cannot inject incomming channel " + channel, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This is executed before Minecraft's channel handler
|
||||
beginInitProtocol = new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel channel) throws Exception {
|
||||
channel.pipeline().addLast(endInitProtocol);
|
||||
}
|
||||
};
|
||||
|
||||
serverChannelHandler = new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
Channel channel = (Channel) msg;
|
||||
|
||||
};
|
||||
|
||||
// This is executed before Minecraft's channel handler
|
||||
beginInitProtocol = new ChannelInitializer<Channel>() {
|
||||
|
||||
@Override
|
||||
protected void initChannel(Channel channel) throws Exception {
|
||||
channel.pipeline().addLast(endInitProtocol);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
serverChannelHandler = 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);
|
||||
}
|
||||
|
||||
};
|
||||
// Prepare to initialize ths channel
|
||||
channel.pipeline().addFirst(beginInitProtocol);
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register bukkit events.
|
||||
*/
|
||||
private void registerBukkitEvents() {
|
||||
listener = new Listener() {
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public final void onPlayerLogin(PlayerLoginEvent e) {
|
||||
if (closed)
|
||||
if (closed)
|
||||
return;
|
||||
|
||||
Channel channel = getChannel(e.getPlayer());
|
||||
|
||||
|
||||
// Don't inject players that have been explicitly uninjected
|
||||
if (!uninjectedChannels.contains(channel)) {
|
||||
injectPlayer(e.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
public final void onPluginDisable(PluginDisableEvent e) {
|
||||
if (e.getPlugin().equals(plugin)) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
plugin.getServer().getPluginManager().registerEvents(listener, plugin);
|
||||
|
||||
this.plugin.getServer().getPluginManager().registerEvents(listener, plugin);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void registerChannelHandler() {
|
||||
Object mcServer = getMinecraftServer.get(Bukkit.getServer());
|
||||
Object serverConnection = getServerConnection.get(mcServer);
|
||||
boolean looking = true;
|
||||
|
||||
try {
|
||||
Field field = Reflection.getParameterizedField(serverConnectionClass, List.class, networkManagerClass);
|
||||
field.setAccessible(true);
|
||||
|
||||
networkManagers = (List<Object>) field.get(serverConnection);
|
||||
} catch (Exception ex) {
|
||||
plugin.getLogger().info("Encountered an exception checking list fields" + ex);
|
||||
MethodInvoker method = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
|
||||
|
||||
networkManagers = (List<Object>) method.invoke(null, serverConnection);
|
||||
}
|
||||
|
||||
if (networkManagers == null) {
|
||||
throw new IllegalArgumentException("Failed to obtain list of network managers");
|
||||
}
|
||||
|
||||
// We need to synchronize against this list
|
||||
networkManagers = (List<Object>) getNetworkMarkers.invoke(null, serverConnection);
|
||||
createServerChannelHandler();
|
||||
|
||||
|
||||
// Find the correct list, or implicitly throw an exception
|
||||
for (int i = 0; looking; i++) {
|
||||
List<Object> list = Reflection.getField(serverConnection.getClass(), List.class, i).get(serverConnection);
|
||||
|
||||
|
||||
for (Object item : list) {
|
||||
if (!ChannelFuture.class.isInstance(item))
|
||||
if (!ChannelFuture.class.isInstance(item))
|
||||
break;
|
||||
|
||||
|
||||
// Channel future that contains the server connection
|
||||
Channel serverChannel = ((ChannelFuture) item).channel();
|
||||
|
||||
Channel serverChannel = ((ChannelFuture)item).channel();
|
||||
|
||||
serverChannels.add(serverChannel);
|
||||
serverChannel.pipeline().addFirst(serverChannelHandler);
|
||||
looking = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void unregisterChannelHandler() {
|
||||
if (serverChannelHandler == null)
|
||||
return;
|
||||
|
||||
|
||||
for (Channel serverChannel : serverChannels) {
|
||||
final ChannelPipeline pipeline = serverChannel.pipeline();
|
||||
|
||||
|
||||
// Remove channel handler
|
||||
serverChannel.eventLoop().execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
pipeline.remove(serverChannelHandler);
|
||||
@@ -258,28 +211,26 @@ public abstract class TinyProtocol {
|
||||
// That's fine
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void registerPlayers(Plugin plugin) {
|
||||
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
||||
injectPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoked when the server is starting to send a packet to a player.
|
||||
* <p>
|
||||
* Note that this is not executed on the main thread.
|
||||
*
|
||||
* @param receiver - the receiving player, NULL for early login/status packets.
|
||||
* @param channel - the channel that received the packet. Never NULL.
|
||||
* @param reciever - the receiving player, NULL for early login/status packets.
|
||||
* @param remoteAddress - remote address of the sending client. Never NULL.
|
||||
* @param packet - the packet being sent.
|
||||
* @return The packet to send instead, or NULL to cancel the transmission.
|
||||
*/
|
||||
public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) {
|
||||
public Object onPacketOutAsync(Player reciever, Channel channel, Object packet) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
@@ -287,7 +238,6 @@ public abstract class TinyProtocol {
|
||||
* Invoked when the server has received a packet from a given player.
|
||||
* <p>
|
||||
* Use {@link Channel#remoteAddress()} to get the remote address of the client.
|
||||
*
|
||||
* @param sender - the player that sent the packet, NULL for early login/status packets.
|
||||
* @param channel - channel that received the packet. Never NULL.
|
||||
* @param packet - the packet being received.
|
||||
@@ -296,145 +246,133 @@ public abstract class TinyProtocol {
|
||||
public Object onPacketInAsync(Player sender, Channel channel, Object packet) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send a packet to a particular player.
|
||||
* <p>
|
||||
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||
*
|
||||
* Note that {@link #onPacketOutAsync(Player, Object)} will be invoked with this packet.
|
||||
* @param player - the destination player.
|
||||
* @param packet - the packet to send.
|
||||
*/
|
||||
public void sendPacket(Player player, Object packet) {
|
||||
sendPacket(getChannel(player), packet);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send a packet to a particular client.
|
||||
* <p>
|
||||
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||
*
|
||||
* Note that {@link #onPacketOutAsync(Player, Object)} will be invoked with this packet.
|
||||
* @param channel - client identified by a channel.
|
||||
* @param packet - the packet to send.
|
||||
*/
|
||||
public void sendPacket(Channel channel, Object packet) {
|
||||
channel.pipeline().writeAndFlush(packet);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pretend that a given packet has been received from a player.
|
||||
* <p>
|
||||
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||
*
|
||||
* Note that {@link #onPacketInAsync(Player, Object)} will be invoked with this packet.
|
||||
* @param player - the player that sent the packet.
|
||||
* @param packet - the packet that will be received by the server.
|
||||
*/
|
||||
public void receivePacket(Player player, Object packet) {
|
||||
receivePacket(getChannel(player), packet);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pretend that a given packet has been received from a given client.
|
||||
* <p>
|
||||
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||
*
|
||||
* Note that {@link #onPacketInAsync(Player, Object)} will be invoked with this packet.
|
||||
* @param channel - client identified by a channel.
|
||||
* @param packet - the packet that will be received by the server.
|
||||
*/
|
||||
public void receivePacket(Channel channel, Object packet) {
|
||||
channel.pipeline().context("encoder").fireChannelRead(packet);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the name of the channel injector, default implementation is "tiny-" + plugin name + "-" + a unique ID.
|
||||
* <p>
|
||||
* Note that this method will only be invoked once. It is no longer necessary to override this to support multiple instances.
|
||||
*
|
||||
* @return A unique channel handler name.
|
||||
*/
|
||||
protected String getHandlerName() {
|
||||
return "tiny-" + plugin.getName() + "-" + ID.incrementAndGet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a custom channel handler to the given player's channel pipeline, allowing us to intercept sent and received packets.
|
||||
* Add a custom channel handler to the given player's channel pipeline,
|
||||
* allowing us to intercept sent and received packets.
|
||||
* <p>
|
||||
* This will automatically be called when a player has logged in.
|
||||
*
|
||||
* @param player - the player to inject.
|
||||
*/
|
||||
public void injectPlayer(Player player) {
|
||||
injectChannelInternal(getChannel(player)).player = player;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a custom channel handler to the given channel.
|
||||
*
|
||||
* @param channel - the channel to inject.
|
||||
* @param player - the channel to inject.
|
||||
* @return The intercepted channel, or NULL if it has already been injected.
|
||||
*/
|
||||
public void injectChannel(Channel channel) {
|
||||
injectChannelInternal(channel);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a custom channel handler to the given channel.
|
||||
*
|
||||
* @param channel - the channel to inject.
|
||||
* @param player - the channel to inject.
|
||||
* @return The packet interceptor.
|
||||
*/
|
||||
private PacketInterceptor injectChannelInternal(Channel channel) {
|
||||
try {
|
||||
PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(handlerName);
|
||||
|
||||
|
||||
// Inject our packet interceptor
|
||||
if (interceptor == null) {
|
||||
interceptor = new PacketInterceptor();
|
||||
channel.pipeline().addBefore("packet_handler", handlerName, interceptor);
|
||||
uninjectedChannels.remove(channel);
|
||||
}
|
||||
|
||||
return interceptor;
|
||||
return interceptor;
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Try again
|
||||
return (PacketInterceptor) channel.pipeline().get(handlerName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the Netty channel associated with a player. This is cached.
|
||||
*
|
||||
* @param player - the player.
|
||||
* @return The Netty channel.
|
||||
*/
|
||||
public Channel getChannel(Player player) {
|
||||
Channel channel = channelLookup.get(player.getName());
|
||||
|
||||
|
||||
// Lookup channel again
|
||||
if (channel == null) {
|
||||
Object connection = getConnection.get(getPlayerHandle.invoke(player));
|
||||
Object manager = getManager.get(connection);
|
||||
|
||||
|
||||
channelLookup.put(player.getName(), channel = getChannel.get(manager));
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Uninject a specific player.
|
||||
*
|
||||
* @param player - the injected player.
|
||||
*/
|
||||
public void uninjectPlayer(Player player) {
|
||||
uninjectChannel(getChannel(player));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Uninject a specific channel.
|
||||
* <p>
|
||||
* This will also disable the automatic channel injection that occurs when a player has properly logged in.
|
||||
*
|
||||
* @param channel - the injected channel.
|
||||
*/
|
||||
public void uninjectChannel(final Channel channel) {
|
||||
@@ -442,95 +380,88 @@ public abstract class TinyProtocol {
|
||||
if (!closed) {
|
||||
uninjectedChannels.add(channel);
|
||||
}
|
||||
|
||||
|
||||
// See ChannelInjector in ProtocolLib, line 590
|
||||
channel.eventLoop().execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
channel.pipeline().remove(handlerName);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the given player has been injected by TinyProtocol.
|
||||
*
|
||||
* @param player - the player.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasInjected(Player player) {
|
||||
return hasInjected(getChannel(player));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the given channel has been injected by TinyProtocol.
|
||||
*
|
||||
* @param channel - the channel.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasInjected(Channel channel) {
|
||||
return channel.pipeline().get(handlerName) != null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cease listening for packets. This is called automatically when your plugin is disabled.
|
||||
*/
|
||||
public final void close() {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
|
||||
|
||||
// Remove our handlers
|
||||
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
||||
uninjectPlayer(player);
|
||||
}
|
||||
|
||||
// Clean up Bukkit
|
||||
HandlerList.unregisterAll(listener);
|
||||
unregisterChannelHandler();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Channel handler that is inserted into the player's channel pipeline, allowing us to intercept sent and received packets.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
private final class PacketInterceptor extends ChannelDuplexHandler {
|
||||
private final class PacketInterceptor extends ChannelDuplexHandler {
|
||||
// Updated by the login event
|
||||
public volatile Player player;
|
||||
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
// Intercept channel
|
||||
final Channel channel = ctx.channel();
|
||||
handleLoginStart(channel, msg);
|
||||
|
||||
|
||||
try {
|
||||
msg = onPacketInAsync(player, channel, msg);
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Error in onPacketInAsync().", e);
|
||||
}
|
||||
|
||||
|
||||
if (msg != null) {
|
||||
super.channelRead(ctx, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
try {
|
||||
msg = onPacketOutAsync(player, ctx.channel(), msg);
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e);
|
||||
}
|
||||
|
||||
|
||||
if (msg != null) {
|
||||
super.write(ctx, msg, promise);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void handleLoginStart(Channel channel, Object packet) {
|
||||
if (PACKET_LOGIN_IN_START.isInstance(packet)) {
|
||||
GameProfile profile = getGameProfile.get(packet);
|
||||
File renamed without changes.
@@ -0,0 +1,165 @@
|
||||
#################
|
||||
## Eclipse
|
||||
#################
|
||||
|
||||
*.pydevproject
|
||||
.project
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
target/
|
||||
.gitignore
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
|
||||
#################
|
||||
## Visual Studio
|
||||
#################
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.vspscc
|
||||
.builds
|
||||
*.dotCover
|
||||
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment this
|
||||
#packages/
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish
|
||||
|
||||
# Others
|
||||
[Bb]in
|
||||
[Oo]bj
|
||||
sql
|
||||
TestResults
|
||||
*.Cache
|
||||
ClientBin
|
||||
stylecop.*
|
||||
~$*
|
||||
*.dbmdl
|
||||
Generated_Code #added for RIA/Silverlight projects
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
|
||||
|
||||
|
||||
############
|
||||
## Windows
|
||||
############
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
|
||||
#############
|
||||
## Python
|
||||
#############
|
||||
|
||||
*.py[co]
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
|
||||
#Translations
|
||||
*.mo
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
@@ -0,0 +1,269 @@
|
||||
<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>
|
||||
<artifactId>ProtocolLib-Legacy</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<name>ProtocolLib-Legacy</name>
|
||||
|
||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>cp1252</project.build.sourceEncoding>
|
||||
<powermock.version>1.5</powermock.version>
|
||||
</properties>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>dmulloy2-releases</id>
|
||||
<url>http://repo.dmulloy2.net/content/repositories/releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>dmulloy2-snapshots</id>
|
||||
<url>http://repo.dmulloy2.net/content/repositories/snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>http://repo.dmulloy2.net/content/groups/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>md_5-repo</id>
|
||||
<url>http://repo.md-5.net/content/repositories/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<defaultGoal>clean install</defaultGoal>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<excludes>
|
||||
<exclude>**/*.java</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>net.sf</pattern>
|
||||
<shadedPattern>com.comphenix.net.sf</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>org.spigotmc:spigot</exclude>
|
||||
<exclude>org.spigotmc:spigot-api</exclude>
|
||||
<exclude>junit:junit</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.1</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<addMavenDescriptor>false</addMavenDescriptor>
|
||||
</archive>
|
||||
<finalName>${project.name}</finalName>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<configuration>
|
||||
<threshold>High</threshold>
|
||||
<effort>Default</effort>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release-sign-artifacts</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>performRelease</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/aadnk/ProtocolLib.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:aadnk/ProtocolLib.git</developerConnection>
|
||||
<url>https://github.com/aadnk/ProtocolLib</url>
|
||||
</scm>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GNU GENERAL PUBLIC LICENSE - Version 2, June 1991</name>
|
||||
<url>http://www.gnu.org/licenses/gpl-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>dmulloy2</id>
|
||||
<name>Dan Mulloy</name>
|
||||
<url>http://shadowvolt.com/</url>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
<role>maintainer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>aadnk</id>
|
||||
<name>Kristian S. Stangeland</name>
|
||||
<email>kr_stang@hotmail.com</email>
|
||||
<url>http://comphenix.net/</url>
|
||||
<roles>
|
||||
<role>former author</role>
|
||||
</roles>
|
||||
<timezone>1</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
<artifactId>cglib-nodep</artifactId>
|
||||
<version>2.2.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.comphenix.executors</groupId>
|
||||
<artifactId>BukkitExecutors</artifactId>
|
||||
<version>1.1-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.7.10-R0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.7.10-R0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.10</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.8.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
/**
|
||||
* Ignore this class.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class Application {
|
||||
private static Thread mainThread;
|
||||
private static boolean primaryMethod = true;
|
||||
|
||||
public static void main(String[] args) {
|
||||
// For now, though we might consider making a proper application
|
||||
System.out.println("This is a Bukkit library. Place it in the plugin-folder and restart the server!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we are running on the main thread.
|
||||
* @return TRUE if we are, FALSE otherwise.
|
||||
*/
|
||||
public static boolean isPrimaryThread() {
|
||||
if (primaryMethod) {
|
||||
try {
|
||||
return Bukkit.isPrimaryThread();
|
||||
} catch (LinkageError e) {
|
||||
primaryMethod = false;
|
||||
}
|
||||
}
|
||||
// Fallback method
|
||||
return Thread.currentThread().equals(mainThread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the calling thread as the primary thread.
|
||||
*/
|
||||
static void registerPrimaryThread() {
|
||||
mainThread = Thread.currentThread();
|
||||
}
|
||||
}
|
||||
+45
-25
@@ -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
|
||||
*/
|
||||
|
||||
@@ -42,63 +42,83 @@ public interface AsynchronousManager {
|
||||
* @param listener - the packet listener that will receive these asynchronous events.
|
||||
* @return An asynchronous handler.
|
||||
*/
|
||||
AsyncListenerHandler registerAsyncHandler(PacketListener listener);
|
||||
public abstract AsyncListenerHandler registerAsyncHandler(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Unregisters and closes the given asynchronous handler.
|
||||
* @param handler - asynchronous handler.
|
||||
*/
|
||||
void unregisterAsyncHandler(AsyncListenerHandler handler);
|
||||
public abstract void unregisterAsyncHandler(AsyncListenerHandler handler);
|
||||
|
||||
/**
|
||||
* Unregisters and closes the first asynchronous handler associated with the given listener.
|
||||
* @param listener - asynchronous listener
|
||||
* @param handler - asynchronous handler.
|
||||
*/
|
||||
void unregisterAsyncHandler(PacketListener listener);
|
||||
public abstract void unregisterAsyncHandler(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Unregisters every asynchronous handler associated with this plugin.
|
||||
* @param plugin - the original plugin.
|
||||
*/
|
||||
void unregisterAsyncHandlers(Plugin plugin);
|
||||
public void unregisterAsyncHandlers(Plugin plugin);
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the ID of the sent server packets that will be
|
||||
* observed by the asynchronous listeners.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getSendingTypes()} instead.
|
||||
* @return Every filtered server packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract Set<Integer> getSendingFilters();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the types of the sent server packets that will be
|
||||
* Retrieves a immutable set containing the types of the sent server packets that will be
|
||||
* observed by the asynchronous listeners.
|
||||
* @return Every filtered server packet.
|
||||
*/
|
||||
Set<PacketType> getSendingTypes();
|
||||
|
||||
public abstract Set<PacketType> getSendingTypes();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the ID of the recieved client packets that will be
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getReceivingTypes()} instead.
|
||||
* observed by the asynchronous listeners.
|
||||
* @return Every filtered client packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract Set<Integer> getReceivingFilters();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the types of the received client packets that will be
|
||||
* observed by the asynchronous listeners.
|
||||
* @return Every filtered client packet.
|
||||
*/
|
||||
Set<PacketType> getReceivingTypes();
|
||||
public abstract Set<PacketType> getReceivingTypes();
|
||||
|
||||
/**
|
||||
* Determine if a given synchronous packet has asynchronous listeners.
|
||||
* @param packet - packet to test.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
boolean hasAsynchronousListeners(PacketEvent packet);
|
||||
public abstract boolean hasAsynchronousListeners(PacketEvent packet);
|
||||
|
||||
/**
|
||||
* Retrieve the default packet stream.
|
||||
* @return Default packet stream.
|
||||
*/
|
||||
PacketStream getPacketStream();
|
||||
public abstract PacketStream getPacketStream();
|
||||
|
||||
/**
|
||||
* Retrieve the default error reporter.
|
||||
* @return Default reporter.
|
||||
*/
|
||||
ErrorReporter getErrorReporter();
|
||||
public abstract ErrorReporter getErrorReporter();
|
||||
|
||||
/**
|
||||
* Remove listeners, close threads and transmit every delayed packet.
|
||||
*/
|
||||
void cleanupAll();
|
||||
public abstract void cleanupAll();
|
||||
|
||||
/**
|
||||
* Signal that a packet is ready to be transmitted.
|
||||
@@ -107,29 +127,29 @@ public interface AsynchronousManager {
|
||||
* has been called previously.
|
||||
* @param packet - packet to signal.
|
||||
*/
|
||||
void signalPacketTransmission(PacketEvent packet);
|
||||
public abstract void signalPacketTransmission(PacketEvent packet);
|
||||
|
||||
/**
|
||||
* Register a synchronous listener that handles packets when they time out.
|
||||
* @param listener - synchronous listener that will handle timed out packets.
|
||||
*/
|
||||
void registerTimeoutHandler(PacketListener listener);
|
||||
public abstract void registerTimeoutHandler(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Unregisters a given timeout listener.
|
||||
* @param listener - the timeout listener to unregister.
|
||||
*/
|
||||
void unregisterTimeoutHandler(PacketListener listener);
|
||||
public abstract void unregisterTimeoutHandler(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Get a immutable set of every registered timeout handler.
|
||||
* @return Set of every registered timeout handler.
|
||||
*/
|
||||
Set<PacketListener> getTimeoutHandlers();
|
||||
public abstract Set<PacketListener> getTimeoutHandlers();
|
||||
|
||||
/**
|
||||
* Get an immutable set of every registered asynchronous packet listener.
|
||||
* @return Set of every asynchronous packet listener.
|
||||
*/
|
||||
Set<PacketListener> getAsyncHandlers();
|
||||
}
|
||||
public abstract Set<PacketListener> getAsyncHandlers();
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.comphenix.protocol.async.AsyncListenerHandler;
|
||||
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.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.injector.server.AbstractInputStreamLookup;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.MethodUtils;
|
||||
import com.comphenix.protocol.reflect.ObjectWriter;
|
||||
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||
import com.comphenix.protocol.reflect.compiler.StructureCompiler;
|
||||
import com.comphenix.protocol.reflect.instances.CollectionGenerator;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.reflect.instances.PrimitiveGenerator;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.ChunkPosition;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
|
||||
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
|
||||
|
||||
/**
|
||||
* Used to fix ClassLoader leaks that may lead to filling up the permanent generation.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class CleanupStaticMembers {
|
||||
// Reports
|
||||
public final static ReportType REPORT_CANNOT_RESET_FIELD = new ReportType("Unable to reset field %s: %s");
|
||||
public final static ReportType REPORT_CANNOT_UNLOAD_CLASS = new ReportType("Unable to unload class %s.");
|
||||
|
||||
private ClassLoader loader;
|
||||
private ErrorReporter reporter;
|
||||
|
||||
public CleanupStaticMembers(ClassLoader loader, ErrorReporter reporter) {
|
||||
this.loader = loader;
|
||||
this.reporter = reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the previous ClassLoader is not leaking.
|
||||
*/
|
||||
public void resetAll() {
|
||||
// This list must always be updated
|
||||
@SuppressWarnings("deprecation")
|
||||
Class<?>[] publicClasses = {
|
||||
AsyncListenerHandler.class, ListeningWhitelist.class, PacketContainer.class,
|
||||
BukkitUnwrapper.class, DefaultInstances.class, CollectionGenerator.class,
|
||||
PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class,
|
||||
BackgroundCompiler.class, StructureCompiler.class,
|
||||
ObjectWriter.class, Packets.Server.class, Packets.Client.class,
|
||||
ChunkPosition.class, WrappedDataWatcher.class, WrappedWatchableObject.class,
|
||||
AbstractInputStreamLookup.class, TemporaryPlayerFactory.class, SpigotPacketInjector.class,
|
||||
MinecraftReflection.class, NbtBinarySerializer.class
|
||||
};
|
||||
|
||||
String[] internalClasses = {
|
||||
"com.comphenix.protocol.events.SerializedOfflinePlayer",
|
||||
"com.comphenix.protocol.injector.player.InjectedServerConnection",
|
||||
"com.comphenix.protocol.injector.player.NetworkFieldInjector",
|
||||
"com.comphenix.protocol.injector.player.NetworkObjectInjector",
|
||||
"com.comphenix.protocol.injector.player.NetworkServerInjector",
|
||||
"com.comphenix.protocol.injector.player.PlayerInjector",
|
||||
"com.comphenix.protocol.injector.EntityUtilities",
|
||||
"com.comphenix.protocol.injector.packet.PacketRegistry",
|
||||
"com.comphenix.protocol.injector.packet.PacketInjector",
|
||||
"com.comphenix.protocol.injector.packet.ReadPacketModifier",
|
||||
"com.comphenix.protocol.injector.StructureCache",
|
||||
"com.comphenix.protocol.reflect.compiler.BoxingHelper",
|
||||
"com.comphenix.protocol.reflect.compiler.MethodDescriptor",
|
||||
"com.comphenix.protocol.wrappers.nbt.WrappedElement",
|
||||
};
|
||||
|
||||
resetClasses(publicClasses);
|
||||
resetClasses(getClasses(loader, internalClasses));
|
||||
}
|
||||
|
||||
private void resetClasses(Class<?>[] classes) {
|
||||
// Reset each class one by one
|
||||
for (Class<?> clazz : classes) {
|
||||
resetClass(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetClass(Class<?> clazz) {
|
||||
for (Field field : clazz.getFields()) {
|
||||
Class<?> type = field.getType();
|
||||
|
||||
// Only check static non-primitive fields. We also skip strings.
|
||||
if (Modifier.isStatic(field.getModifiers()) &&
|
||||
!type.isPrimitive() && !type.equals(String.class) &&
|
||||
!type.equals(ReportType.class)) {
|
||||
|
||||
try {
|
||||
setFinalStatic(field, null);
|
||||
} catch (IllegalAccessException e) {
|
||||
// Just inform the player
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_RESET_FIELD).error(e).messageParam(field.getName(), e.getMessage())
|
||||
);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HACK! HAACK!
|
||||
private static void setFinalStatic(Field field, Object newValue) throws IllegalAccessException {
|
||||
int modifier = field.getModifiers();
|
||||
boolean isFinal = Modifier.isFinal(modifier);
|
||||
|
||||
Field modifiersField = isFinal ? FieldUtils.getField(Field.class, "modifiers", true) : null;
|
||||
|
||||
// We have to remove the final field first
|
||||
if (isFinal) {
|
||||
FieldUtils.writeField(modifiersField, field, modifier & ~Modifier.FINAL, true);
|
||||
}
|
||||
|
||||
// Now we can safely modify the field
|
||||
FieldUtils.writeStaticField(field, newValue, true);
|
||||
|
||||
// Revert modifier
|
||||
if (isFinal) {
|
||||
FieldUtils.writeField(modifiersField, field, modifier, true);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?>[] getClasses(ClassLoader loader, String[] names) {
|
||||
List<Class<?>> output = new ArrayList<Class<?>>();
|
||||
|
||||
for (String name : names) {
|
||||
try {
|
||||
output.add(loader.loadClass(name));
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Warn the user
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_UNLOAD_CLASS).error(e).messageParam(name));
|
||||
}
|
||||
}
|
||||
|
||||
return output.toArray(new Class<?>[0]);
|
||||
}
|
||||
}
|
||||
+25
-30
@@ -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
|
||||
*/
|
||||
|
||||
@@ -41,9 +41,9 @@ abstract class CommandBase implements CommandExecutor {
|
||||
|
||||
public static final String PERMISSION_ADMIN = "protocol.admin";
|
||||
|
||||
private final String permission;
|
||||
private final String name;
|
||||
private final int minimumArgumentCount;
|
||||
private String permission;
|
||||
private String name;
|
||||
private int minimumArgumentCount;
|
||||
|
||||
protected ErrorReporter reporter;
|
||||
|
||||
@@ -66,7 +66,6 @@ abstract class CommandBase implements CommandExecutor {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_UNEXPECTED_COMMAND).messageParam(this));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (permission != null && !sender.hasPermission(permission)) {
|
||||
sender.sendMessage(ChatColor.RED + "You haven't got permission to run this command.");
|
||||
return true;
|
||||
@@ -76,12 +75,13 @@ abstract class CommandBase implements CommandExecutor {
|
||||
if (args != null && args.length >= minimumArgumentCount) {
|
||||
return handleCommand(sender, args);
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "Insufficient arguments. You need at least " + minimumArgumentCount);
|
||||
sender.sendMessage(ChatColor.RED + "Insufficient commands. You need at least " + minimumArgumentCount);
|
||||
return false;
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_COMMAND_ERROR).error(ex).messageParam(name).callerParam(sender, label, args)
|
||||
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_COMMAND_ERROR).error(e).messageParam(name).callerParam(sender, label, args)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@@ -97,23 +97,18 @@ abstract class CommandBase implements CommandExecutor {
|
||||
Boolean result = null;
|
||||
|
||||
if (!arguments.isEmpty()) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
if (result != null) arguments.poll();
|
||||
if (result != null)
|
||||
arguments.poll();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -124,7 +119,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<>(Arrays.asList(args).subList(start, args.length));
|
||||
return new ArrayDeque<String>(Arrays.asList(args).subList(start, args.length));
|
||||
}
|
||||
|
||||
/**
|
||||
+7
-6
@@ -2,7 +2,6 @@ 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;
|
||||
@@ -27,6 +26,7 @@ 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 = new HashSet<>(packets);
|
||||
this.packets = Sets.newHashSet(packets);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +103,7 @@ public class CommandFilter extends CommandBase {
|
||||
* @return Set of packets this filter applies to.
|
||||
*/
|
||||
public Set<PacketType> getRanges() {
|
||||
return new HashSet<>(packets);
|
||||
return Sets.newHashSet(packets);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -295,7 +295,7 @@ public class CommandFilter extends CommandBase {
|
||||
@Override
|
||||
public boolean handle(PacketEvent event, Filter filter, Exception ex) {
|
||||
reporter.reportMinimal(plugin, "filterEvent(PacketEvent)", ex, event);
|
||||
reporter.reportWarning(this,
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_FILTER_REMOVED_FOR_ERROR).messageParam(filter.getName(), ex.getClass().getSimpleName())
|
||||
);
|
||||
return false;
|
||||
@@ -321,6 +321,7 @@ public class CommandFilter extends CommandBase {
|
||||
* @param event - the event.
|
||||
* @param handler - failure handler.
|
||||
* @return TRUE if we should, FALSE otherwise.
|
||||
* @throws FilterFailedException If one of the filters failed.
|
||||
*/
|
||||
public boolean filterEvent(PacketEvent event, FilterFailedHandler handler) {
|
||||
for (Iterator<Filter> it = filters.iterator(); it.hasNext(); ) {
|
||||
@@ -388,7 +389,7 @@ public class CommandFilter extends CommandBase {
|
||||
|
||||
// Make sure we can use the conversable interface
|
||||
if (sender instanceof Conversable) {
|
||||
final MultipleLinesPrompt prompt =
|
||||
final MultipleLinesPrompt prompt =
|
||||
new MultipleLinesPrompt(new CompilationSuccessCanceller(), "function(event, packet) {");
|
||||
|
||||
new ConversationFactory(plugin).
|
||||
@@ -423,7 +424,7 @@ public class CommandFilter extends CommandBase {
|
||||
whom.sendRawMessage(ChatColor.RED + "Cancelled filter.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(this,
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_HANDLE_CONVERSATION).error(e).callerParam(event)
|
||||
);
|
||||
}
|
||||
+107
-22
@@ -22,7 +22,6 @@ 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;
|
||||
@@ -30,6 +29,8 @@ import java.util.WeakHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -41,11 +42,18 @@ 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.
|
||||
@@ -69,6 +77,11 @@ class CommandPacket extends CommandBase {
|
||||
*/
|
||||
public static final int PAGE_LINE_COUNT = 9;
|
||||
|
||||
/**
|
||||
* Number of bytes before we do a hex dump.
|
||||
*/
|
||||
private static final int HEX_DUMP_THRESHOLD = 256;
|
||||
|
||||
private Plugin plugin;
|
||||
private Logger logger;
|
||||
private ProtocolManager manager;
|
||||
@@ -114,7 +127,13 @@ class CommandPacket extends CommandBase {
|
||||
* @return TRUE if the message was sent successfully, FALSE otherwise.
|
||||
*/
|
||||
public void sendMessageSilently(CommandSender receiver, String message) {
|
||||
chatter.sendMessageSilently(receiver, message);
|
||||
try {
|
||||
chatter.sendMessageSilently(receiver, message);
|
||||
} catch (InvocationTargetException e) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_SEND_MESSAGE).error(e).callerParam(receiver, message)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,7 +142,13 @@ class CommandPacket extends CommandBase {
|
||||
* @param permission - permission required to receieve the message. NULL to target everyone.
|
||||
*/
|
||||
public void broadcastMessageSilently(String message, String permission) {
|
||||
chatter.broadcastMessageSilently(message, permission);
|
||||
try {
|
||||
chatter.broadcastMessageSilently(message, permission);
|
||||
} catch (InvocationTargetException e) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_SEND_MESSAGE).error(e).callerParam(message, permission)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void printPage(CommandSender sender, int pageIndex) {
|
||||
@@ -287,7 +312,7 @@ class CommandPacket extends CommandBase {
|
||||
}
|
||||
|
||||
private Set<PacketType> filterTypes(Set<PacketType> types, Sender sender) {
|
||||
final Set<PacketType> result = new HashSet<>();
|
||||
Set<PacketType> result = Sets.newHashSet();
|
||||
|
||||
for (PacketType type : types) {
|
||||
if (type.getSender() == sender) {
|
||||
@@ -346,7 +371,7 @@ class CommandPacket extends CommandBase {
|
||||
logger.info("Initial packet:\n" + original + " -> ");
|
||||
}
|
||||
|
||||
logger.info(shortDescription + ":\n" + HexDumper.getPacketDescription(
|
||||
logger.info(shortDescription + ":\n" + getPacketDescription(
|
||||
event.getPacket())
|
||||
);
|
||||
} catch (IllegalAccessException e) {
|
||||
@@ -403,7 +428,7 @@ class CommandPacket extends CommandBase {
|
||||
*/
|
||||
private void savePacketState(PacketEvent event) {
|
||||
try {
|
||||
originalPackets.put(event, HexDumper.getPacketDescription(event.getPacket()));
|
||||
originalPackets.put(event, getPacketDescription(event.getPacket()));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot read packet.", e);
|
||||
}
|
||||
@@ -425,6 +450,68 @@ class CommandPacket extends CommandBase {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a detailed string representation of the given packet.
|
||||
* @param packetContainer - the packet to describe.
|
||||
* @return The detailed description.
|
||||
* @throws IllegalAccessException An error occured.
|
||||
*/
|
||||
public String getPacketDescription(PacketContainer packetContainer) throws IllegalAccessException {
|
||||
Object packet = packetContainer.getHandle();
|
||||
Class<?> clazz = packet.getClass();
|
||||
|
||||
// Get the first Minecraft super class
|
||||
while (clazz != null && clazz != Object.class &&
|
||||
(!MinecraftReflection.isMinecraftClass(clazz) ||
|
||||
Factory.class.isAssignableFrom(clazz))) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
|
||||
return PrettyPrinter.printObject(packet, clazz, MinecraftReflection.getPacketClass(), PrettyPrinter.RECURSE_DEPTH, new ObjectPrinter() {
|
||||
@Override
|
||||
public boolean print(StringBuilder output, Object value) {
|
||||
// Special case
|
||||
if (value instanceof byte[]) {
|
||||
byte[] data = (byte[]) value;
|
||||
|
||||
if (data.length > HEX_DUMP_THRESHOLD) {
|
||||
output.append("[");
|
||||
HexDumper.defaultDumper().appendTo(output, data);
|
||||
output.append("]");
|
||||
return true;
|
||||
}
|
||||
} else if (value != null) {
|
||||
EquivalentConverter<Object> converter = findConverter(value.getClass());
|
||||
|
||||
if (converter != null) {
|
||||
output.append(converter.getSpecific(value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the closest equivalent converter to a specific class.
|
||||
* @param clazz - the class.
|
||||
* @return The closest converter, or NULL if not found,
|
||||
*/
|
||||
private EquivalentConverter<Object> findConverter(Class<?> clazz) {
|
||||
Map<Class<?>, EquivalentConverter<Object>> converters = BukkitConverters.getConvertersForGeneric();
|
||||
|
||||
while (clazz != null) {
|
||||
EquivalentConverter<Object> result = converters.get(clazz);
|
||||
|
||||
if (result != null)
|
||||
return result;
|
||||
else
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public PacketListener updatePacketListener() {
|
||||
if (listener != null) {
|
||||
@@ -442,21 +529,19 @@ class CommandPacket extends CommandBase {
|
||||
return listener;
|
||||
}
|
||||
|
||||
private SubCommand parseCommand(Deque<String> arguments)
|
||||
{
|
||||
final String text = arguments.remove().toLowerCase();
|
||||
|
||||
switch (text) {
|
||||
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.");
|
||||
}
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager;
|
||||
import com.comphenix.protocol.timing.TimingReportGenerator;
|
||||
|
||||
/**
|
||||
* Handles the "protocol" administration command.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class CommandProtocol extends CommandBase {
|
||||
/**
|
||||
* Name of this command.
|
||||
*/
|
||||
public static final String NAME = "protocol";
|
||||
|
||||
private Plugin plugin;
|
||||
private ProtocolConfig config;
|
||||
|
||||
public CommandProtocol(ErrorReporter reporter, Plugin plugin, ProtocolConfig config) {
|
||||
super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 1);
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean handleCommand(CommandSender sender, String[] args) {
|
||||
String subCommand = args[0];
|
||||
|
||||
// Only return TRUE if we executed the correct command
|
||||
if (subCommand.equalsIgnoreCase("config") || subCommand.equalsIgnoreCase("reload"))
|
||||
reloadConfiguration(sender);
|
||||
else if (subCommand.equalsIgnoreCase("timings"))
|
||||
toggleTimings(sender, args);
|
||||
else if (subCommand.equalsIgnoreCase("listeners"))
|
||||
printListeners(sender, args);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Display every listener on the server
|
||||
private void printListeners(final CommandSender sender, String[] args) {
|
||||
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
for (PacketListener listener : manager.getPacketListeners()) {
|
||||
sender.sendMessage(ChatColor.GOLD + "Packet listeners:");
|
||||
sender.sendMessage(ChatColor.GOLD + " " + listener);
|
||||
}
|
||||
// Along with every asynchronous listener
|
||||
for (PacketListener listener : manager.getAsynchronousManager().getAsyncHandlers()) {
|
||||
sender.sendMessage(ChatColor.GOLD + "Asynchronous listeners:");
|
||||
sender.sendMessage(ChatColor.GOLD + " " + listener);
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleTimings(CommandSender sender, String[] args) {
|
||||
TimedListenerManager manager = TimedListenerManager.getInstance();
|
||||
boolean state = !manager.isTiming(); // toggle
|
||||
|
||||
// Parse the boolean parameter
|
||||
if (args.length == 2) {
|
||||
Boolean parsed = parseBoolean(toQueue(args, 2), "start");
|
||||
|
||||
if (parsed != null) {
|
||||
state = parsed;
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "Specify a state: ON or OFF.");
|
||||
return;
|
||||
}
|
||||
} else if (args.length > 2) {
|
||||
sender.sendMessage(ChatColor.RED + "Too many parameters.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Now change the state
|
||||
if (state) {
|
||||
if (manager.startTiming())
|
||||
sender.sendMessage(ChatColor.GOLD + "Started timing packet listeners.");
|
||||
else
|
||||
sender.sendMessage(ChatColor.RED + "Packet timing already started.");
|
||||
} else {
|
||||
if (manager.stopTiming()) {
|
||||
saveTimings(manager);
|
||||
sender.sendMessage(ChatColor.GOLD + "Stopped and saved result in plugin folder.");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "Packet timing already stopped.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveTimings(TimedListenerManager manager) {
|
||||
try {
|
||||
File destination = new File(plugin.getDataFolder(), "Timings - " + System.currentTimeMillis() + ".txt");
|
||||
TimingReportGenerator generator = new TimingReportGenerator();
|
||||
|
||||
// Print to a text file
|
||||
generator.saveTo(destination, manager);
|
||||
manager.clear();
|
||||
|
||||
} catch (IOException e) {
|
||||
reporter.reportMinimal(plugin, "saveTimings()", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent further automatic updates until the next delay.
|
||||
*/
|
||||
public void updateFinished() {
|
||||
long currentTime = System.currentTimeMillis() / ProtocolLibrary.MILLI_PER_SECOND;
|
||||
|
||||
config.setAutoLastTime(currentTime);
|
||||
config.saveAll();
|
||||
}
|
||||
|
||||
public void reloadConfiguration(CommandSender sender) {
|
||||
plugin.reloadConfig();
|
||||
sender.sendMessage(ChatColor.YELLOW + "Reloaded configuration!");
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import 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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters)
|
||||
throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters)
|
||||
throws InvocationTargetException;
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Simulate recieving a certain packet from a given player.
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
}
|
||||
@@ -0,0 +1,951 @@
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import com.comphenix.protocol.annotations.Spigot;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.ObjectEnum;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.base.Objects;
|
||||
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 com.google.common.util.concurrent.Futures;
|
||||
|
||||
/**
|
||||
* Represents the type of a packet in a specific protocol.
|
||||
* <p>
|
||||
* Note that vanilla Minecraft reuses packet IDs per protocol (ping, game, login), so you cannot
|
||||
* rely on IDs alone.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PacketType implements Serializable, Comparable<PacketType> {
|
||||
// Increment whenever the type changes
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Represents an unknown legacy packet ID.
|
||||
*/
|
||||
public static final int UNKNOWN_PACKET = -1;
|
||||
|
||||
/**
|
||||
* Packets sent during handshake.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Handshake {
|
||||
private static final Protocol PROTOCOL = Protocol.HANDSHAKING;
|
||||
|
||||
/**
|
||||
* Incoming packets.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Client extends ObjectEnum<PacketType> {
|
||||
private final static Sender SENDER = Sender.CLIENT;
|
||||
/**
|
||||
* Legacy name: HANDSHAKE.
|
||||
*/
|
||||
public static final PacketType SET_PROTOCOL = new PacketType(PROTOCOL, SENDER, 0x00, 2);
|
||||
|
||||
private final static Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Client() { super(PacketType.class); }
|
||||
|
||||
public static Client getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An empty enum, as the server will not send any packets in this protocol.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Server extends ObjectEnum<PacketType> {
|
||||
private final static Sender SENDER = Sender.CLIENT;
|
||||
private final static Server INSTANCE = new Server();
|
||||
private Server() { super(PacketType.class); }
|
||||
|
||||
public static Server getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
}
|
||||
}
|
||||
|
||||
public static Protocol getProtocol() {
|
||||
return PROTOCOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Packets sent and received when logged into the game.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Play {
|
||||
private static final Protocol PROTOCOL = Protocol.PLAY;
|
||||
|
||||
/**
|
||||
* Outgoing packets.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Server extends ObjectEnum<PacketType> {
|
||||
private final static Sender SENDER = Sender.SERVER;
|
||||
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x00, 0);
|
||||
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x01, 1);
|
||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x02, 3);
|
||||
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x03, 4);
|
||||
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x04, 5);
|
||||
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x05, 6);
|
||||
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x06, 8);
|
||||
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x07, 9);
|
||||
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x08, 13);
|
||||
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x09, 16);
|
||||
/**
|
||||
* Note that this was Packets.Server.ENTITY_LOCATION_ACTION.
|
||||
*/
|
||||
public static final PacketType BED = new PacketType(PROTOCOL, SENDER, 0x0A, 17);
|
||||
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x0B, 18);
|
||||
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x0C, 20);
|
||||
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x0D, 22);
|
||||
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0E, 23);
|
||||
public static final PacketType SPAWN_ENTITY_LIVING = new PacketType(PROTOCOL, SENDER, 0x0F, 24);
|
||||
public static final PacketType SPAWN_ENTITY_PAINTING = new PacketType(PROTOCOL, SENDER, 0x10, 25);
|
||||
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB =
|
||||
new PacketType(PROTOCOL, SENDER, 0x11, 26);
|
||||
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x12, 28);
|
||||
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x13, 29);
|
||||
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 0x14, 30);
|
||||
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x15, 31);
|
||||
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x16, 32);
|
||||
public static final PacketType ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x17, 33);
|
||||
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x18, 34);
|
||||
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x19, 35);
|
||||
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1A, 38);
|
||||
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x1B, 39);
|
||||
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x1C, 40);
|
||||
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x1D, 41);
|
||||
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x1E, 42);
|
||||
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x1F, 43);
|
||||
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x20, 44);
|
||||
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x21, 51);
|
||||
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x22, 52);
|
||||
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x23, 53);
|
||||
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x24, 54);
|
||||
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x25, 55);
|
||||
public static final PacketType MAP_CHUNK_BULK = new PacketType(PROTOCOL, SENDER, 0x26, 56);
|
||||
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x27, 60);
|
||||
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x28, 61);
|
||||
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x29, 62);
|
||||
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x2A, 63);
|
||||
/**
|
||||
* Note that this was Packets.Server.BED.
|
||||
*/
|
||||
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x2B, 70);
|
||||
public static final PacketType SPAWN_ENTITY_WEATHER = new PacketType(PROTOCOL, SENDER, 0x2C, 71);
|
||||
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x2D, 100);
|
||||
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x2E, 101);
|
||||
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x2F, 103);
|
||||
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x30, 104);
|
||||
public static final PacketType CRAFT_PROGRESS_BAR = new PacketType(PROTOCOL, SENDER, 0x31, 105);
|
||||
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x32, 106);
|
||||
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x33, 130);
|
||||
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x34, 131);
|
||||
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x35, 132);
|
||||
public static final PacketType OPEN_SIGN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x36, 133);
|
||||
public static final PacketType STATISTICS = new PacketType(PROTOCOL, SENDER, 0x37, 200);
|
||||
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x38, 201);
|
||||
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x39, 202);
|
||||
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x3A, 203);
|
||||
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x3B, 206);
|
||||
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x3C, 207);
|
||||
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE =
|
||||
new PacketType(PROTOCOL, SENDER, 0x3D, 208);
|
||||
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x3E, 209);
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x3F, 250);
|
||||
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x40, 255);
|
||||
|
||||
@Spigot(minimumBuild = 1628)
|
||||
public static final PacketType TITLE = new PacketType(PROTOCOL, SENDER, 0x45, -1);
|
||||
@Spigot(minimumBuild = 1628)
|
||||
public static final PacketType TAB_HEADER = new PacketType(PROTOCOL, SENDER, 0x47, -1);
|
||||
@Spigot(minimumBuild = 1628)
|
||||
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x48, -1);
|
||||
|
||||
// The instance must
|
||||
private final static Server INSTANCE = new Server();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Server() { super(PacketType.class); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
}
|
||||
public static Server getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Incoming packets.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Client extends ObjectEnum<PacketType> {
|
||||
private final static Sender SENDER = Sender.CLIENT;
|
||||
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x00, 0);
|
||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x01, 3);
|
||||
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x02, 7);
|
||||
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 0x03, 10);
|
||||
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x04, 11);
|
||||
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x05, 12);
|
||||
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x06, 13);
|
||||
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x07, 14);
|
||||
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x08, 15);
|
||||
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x09, 16);
|
||||
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x0A, 18);
|
||||
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x0B, 19);
|
||||
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x0C, 27);
|
||||
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0D, 101);
|
||||
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x0E, 102);
|
||||
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x0F, 106);
|
||||
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x10, 107);
|
||||
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x11, 108);
|
||||
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x12, 130);
|
||||
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x13, 202);
|
||||
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x14, 203);
|
||||
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x15, 204);
|
||||
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x16, 205);
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x17, 250);
|
||||
|
||||
@Spigot(minimumBuild = 1628)
|
||||
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x19, -1);
|
||||
|
||||
private final static Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Client() { super(PacketType.class); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
}
|
||||
public static Client getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
public static Protocol getProtocol() {
|
||||
return PROTOCOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Packets sent and received when querying the server in the multiplayer menu.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Status {
|
||||
private static final Protocol PROTOCOL = Protocol.STATUS;
|
||||
|
||||
/**
|
||||
* Outgoing packets.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Server extends ObjectEnum<PacketType> {
|
||||
private final static Sender SENDER = Sender.SERVER;
|
||||
|
||||
public static final PacketType OUT_SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, 255);
|
||||
@SuppressWarnings("deprecation")
|
||||
public static final PacketType OUT_PING = new PacketType(PROTOCOL, SENDER, 0x01, Packets.Server.PING_TIME);
|
||||
|
||||
private final static Server INSTANCE = new Server();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Server() { super(PacketType.class); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
}
|
||||
public static Server getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Incoming packets.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Client extends ObjectEnum<PacketType> {
|
||||
private final static Sender SENDER = Sender.CLIENT;
|
||||
|
||||
public static final PacketType IN_START = new PacketType(PROTOCOL, SENDER, 0x00, 254);
|
||||
@SuppressWarnings("deprecation")
|
||||
public static final PacketType IN_PING = new PacketType(PROTOCOL, SENDER, 0x01, Packets.Client.PING_TIME);
|
||||
|
||||
private final static Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Client() { super(PacketType.class); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
}
|
||||
public static Client getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
public static Protocol getProtocol() {
|
||||
return PROTOCOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Packets sent and received when logging in to the server.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Login {
|
||||
private static final Protocol PROTOCOL = Protocol.LOGIN;
|
||||
|
||||
/**
|
||||
* Outgoing packets.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Server extends ObjectEnum<PacketType> {
|
||||
private final static Sender SENDER = Sender.SERVER;
|
||||
|
||||
public static final PacketType DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, 255);
|
||||
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, 253);
|
||||
@SuppressWarnings("deprecation")
|
||||
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, Packets.Server.LOGIN_SUCCESS);
|
||||
|
||||
@Spigot(minimumBuild = 1628)
|
||||
public static final PacketType LOGIN_COMPRESSION = new PacketType(PROTOCOL, SENDER, 0x03, -1);
|
||||
|
||||
private final static Server INSTANCE = new Server();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Server() { super(PacketType.class); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
}
|
||||
public static Server getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Incoming packets.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Client extends ObjectEnum<PacketType> {
|
||||
private final static Sender SENDER = Sender.CLIENT;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, Packets.Client.LOGIN_START);
|
||||
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, 252);
|
||||
|
||||
private final static Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Client() { super(PacketType.class); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
}
|
||||
public static Client getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
public static Protocol getProtocol() {
|
||||
return PROTOCOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains every packet Minecraft 1.6.4 packet removed in Minecraft 1.7.2.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class Legacy {
|
||||
private static final Protocol PROTOCOL = Protocol.LEGACY;
|
||||
|
||||
/**
|
||||
* Outgoing packets.
|
||||
* @author Kristian
|
||||
*/
|
||||
// Missing server packets: [10, 11, 12, 21, 107, 252]
|
||||
public static class Server extends ObjectEnum<PacketType> {
|
||||
private final static Sender SENDER = Sender.SERVER;
|
||||
|
||||
public static final PacketType PLAYER_FLYING = PacketType.newLegacy(SENDER, 10);
|
||||
public static final PacketType PLAYER_POSITION = PacketType.newLegacy(SENDER, 11);
|
||||
public static final PacketType PLAYER_POSITON_LOOK = PacketType.newLegacy(SENDER, 12);
|
||||
/**
|
||||
* Removed in Minecraft 1.4.6.
|
||||
*/
|
||||
public static final PacketType PICKUP_SPAWN = PacketType.newLegacy(SENDER, 21);
|
||||
/**
|
||||
* Removed in Minecraft 1.7.2
|
||||
*/
|
||||
public static final PacketType SET_CREATIVE_SLOT = PacketType.newLegacy(SENDER, 107);
|
||||
|
||||
/**
|
||||
* Removed in Minecraft 1.7.2
|
||||
*/
|
||||
public static final PacketType KEY_RESPONSE = PacketType.newLegacy(SENDER, 252);
|
||||
|
||||
private final static Server INSTANCE = new Server();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Server() {
|
||||
super(PacketType.class);
|
||||
}
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
}
|
||||
public static Server getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Incoming packets.
|
||||
* @author Kristian
|
||||
*/
|
||||
// Missing client packets: [1, 9, 255]
|
||||
public static class Client extends ObjectEnum<PacketType> {
|
||||
private final static Sender SENDER = Sender.CLIENT;
|
||||
|
||||
public static final PacketType LOGIN = PacketType.newLegacy(SENDER, 1);
|
||||
public static final PacketType RESPAWN = PacketType.newLegacy(SENDER, 9);
|
||||
public static final PacketType DISCONNECT = PacketType.newLegacy(SENDER, 255);
|
||||
|
||||
private final static Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Client() { super(PacketType.class); }
|
||||
|
||||
public static Sender getSender() {
|
||||
return SENDER;
|
||||
}
|
||||
public static Client getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
public static Protocol getProtocol() {
|
||||
return PROTOCOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the different protocol or connection states.
|
||||
* @author Kristian
|
||||
*/
|
||||
public enum Protocol {
|
||||
HANDSHAKING,
|
||||
PLAY,
|
||||
STATUS,
|
||||
LOGIN,
|
||||
|
||||
/**
|
||||
* Only for packets removed in Minecraft 1.7.2
|
||||
*/
|
||||
LEGACY;
|
||||
|
||||
/**
|
||||
* Retrieve the correct protocol enum from a given vanilla enum instance.
|
||||
* @param vanilla - the vanilla protocol enum instance.
|
||||
* @return The corresponding protocol.
|
||||
*/
|
||||
public static Protocol fromVanilla(Enum<?> vanilla) {
|
||||
String name = vanilla.name();
|
||||
|
||||
if ("HANDSHAKING".equals(name))
|
||||
return HANDSHAKING;
|
||||
if ("PLAY".equals(name))
|
||||
return PLAY;
|
||||
if ("STATUS".equals(name))
|
||||
return STATUS;
|
||||
if ("LOGIN".equals(name))
|
||||
return LOGIN;
|
||||
throw new IllegalArgumentException("Unrecognized vanilla enum " + vanilla);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the sender of this packet type.
|
||||
* @author Kristian
|
||||
*
|
||||
*/
|
||||
public enum Sender {
|
||||
/**
|
||||
* Indicates that packets of this type will be sent by connected clients.
|
||||
*/
|
||||
CLIENT,
|
||||
|
||||
/**
|
||||
* Indicate that packets of this type will be sent by the current server.
|
||||
*/
|
||||
SERVER;
|
||||
|
||||
/**
|
||||
* Retrieve the equivialent connection side.
|
||||
* @return The connection side.
|
||||
*/
|
||||
public ConnectionSide toSide() {
|
||||
return this == CLIENT ? ConnectionSide.CLIENT_SIDE : ConnectionSide.SERVER_SIDE;
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup of packet types
|
||||
private static PacketTypeLookup LOOKUP;
|
||||
|
||||
/**
|
||||
* Protocol version of all the current IDs.
|
||||
*/
|
||||
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.WORLD_UPDATE;
|
||||
|
||||
private final Protocol protocol;
|
||||
private final Sender sender;
|
||||
private final int currentId;
|
||||
private final int legacyId;
|
||||
private final MinecraftVersion version;
|
||||
|
||||
/**
|
||||
* Retrieve the current packet/legacy lookup.
|
||||
* @return The packet type lookup.
|
||||
*/
|
||||
private static PacketTypeLookup getLookup() {
|
||||
if (LOOKUP == null) {
|
||||
LOOKUP = new PacketTypeLookup().
|
||||
addPacketTypes(Handshake.Client.getInstance()).
|
||||
addPacketTypes(Handshake.Server.getInstance()).
|
||||
addPacketTypes(Play.Client.getInstance()).
|
||||
addPacketTypes(Play.Server.getInstance()).
|
||||
addPacketTypes(Status.Client.getInstance()).
|
||||
addPacketTypes(Status.Server.getInstance()).
|
||||
addPacketTypes(Login.Client.getInstance()).
|
||||
addPacketTypes(Login.Server.getInstance()).
|
||||
addPacketTypes(Legacy.Client.getInstance()).
|
||||
addPacketTypes(Legacy.Server.getInstance());
|
||||
}
|
||||
return LOOKUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find every packet type known to the current version of ProtocolLib.
|
||||
* @return Every packet type.
|
||||
*/
|
||||
public static Iterable<PacketType> values() {
|
||||
List<Iterable<? extends PacketType>> sources = Lists.newArrayList();
|
||||
sources.add(Handshake.Client.getInstance());
|
||||
sources.add(Handshake.Server.getInstance());
|
||||
sources.add(Play.Client.getInstance());
|
||||
sources.add(Play.Server.getInstance());
|
||||
sources.add(Status.Client.getInstance());
|
||||
sources.add(Status.Server.getInstance());
|
||||
sources.add(Login.Client.getInstance());
|
||||
sources.add(Login.Server.getInstance());
|
||||
|
||||
// Add the missing types in earlier versions
|
||||
if (!MinecraftReflection.isUsingNetty()) {
|
||||
sources.add(Legacy.Client.getInstance());
|
||||
sources.add(Legacy.Server.getInstance());
|
||||
}
|
||||
return Iterables.concat(sources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a packet type from a legacy (1.6.4 and below) packet ID.
|
||||
* @param packetId - the legacy packet ID.
|
||||
* @return The corresponding packet type.
|
||||
* @throws IllegalArgumentException If the legacy packet could not be found.
|
||||
*/
|
||||
public static PacketType findLegacy(int packetId) {
|
||||
PacketType type = getLookup().getFromLegacy(packetId);
|
||||
|
||||
if (type != null)
|
||||
return type;
|
||||
throw new IllegalArgumentException("Cannot find legacy packet " + packetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a packet type from a legacy (1.6.4 and below) packet ID.
|
||||
* @param packetId - the legacy packet ID.
|
||||
* @param preference - the preferred sender, or NULL for any arbitrary sender.
|
||||
* @return The corresponding packet type.
|
||||
* @throws IllegalArgumentException If the legacy packet could not be found.
|
||||
*/
|
||||
public static PacketType findLegacy(int packetId, Sender preference) {
|
||||
if (preference == null)
|
||||
return findLegacy(packetId);
|
||||
PacketType type = getLookup().getFromLegacy(packetId, preference);
|
||||
|
||||
if (type != null)
|
||||
return type;
|
||||
throw new IllegalArgumentException("Cannot find legacy packet " + packetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given legacy packet exists.
|
||||
* @param packetId - the legacy packet ID.
|
||||
* @param preference - the sender preference.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public static boolean hasLegacy(int packetId) {
|
||||
return getLookup().getFromLegacy(packetId) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a packet type from a protocol, sender and packet ID.
|
||||
* <p>
|
||||
* It is usually better to access the packet types statically, like so:
|
||||
* <ul>
|
||||
* <li>{@link PacketType.Play.Server#SPAWN_ENTITY}
|
||||
* </ul>
|
||||
* @param protocol - the current protocol.
|
||||
* @param sender - the sender.
|
||||
* @param packetId - the packet ID.
|
||||
* @return The corresponding packet type.
|
||||
* @throws IllegalArgumentException If the current packet could not be found.
|
||||
*/
|
||||
public static PacketType findCurrent(Protocol protocol, Sender sender, int packetId) {
|
||||
PacketType type = getLookup().getFromCurrent(protocol, sender, packetId);
|
||||
|
||||
if (type != null)
|
||||
return type;
|
||||
throw new IllegalArgumentException("Cannot find packet " + packetId +
|
||||
"(Protocol: " + protocol + ", Sender: " + sender + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given packet exists.
|
||||
* @param protocol - the protocol.
|
||||
* @param sender - the sender.
|
||||
* @param packetId - the packet ID.
|
||||
* @return TRUE if it exists, FALSE otherwise.
|
||||
*/
|
||||
public static boolean hasCurrent(Protocol protocol, Sender sender, int packetId) {
|
||||
return getLookup().getFromCurrent(protocol, sender, packetId) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a packet type from a legacy ID.
|
||||
* <p>
|
||||
* If no associated packet type could be found, a new will be registered under LEGACY.
|
||||
* @param id - the legacy ID.
|
||||
* @param sender - the sender of the packet, or NULL if unknown.
|
||||
* @return The packet type.
|
||||
* @throws IllegalArgumentException If the sender is NULL and the packet doesn't exist.
|
||||
*/
|
||||
public static PacketType fromLegacy(int id, Sender sender) {
|
||||
PacketType type = getLookup().getFromLegacy(id, sender);
|
||||
|
||||
if (type == null) {
|
||||
if (sender == null)
|
||||
throw new IllegalArgumentException("Cannot find legacy packet " + id);
|
||||
type = newLegacy(sender, id);
|
||||
|
||||
// As below
|
||||
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a packet type from a protocol, sender and packet ID.
|
||||
* <p>
|
||||
* The packet will automatically be registered if its missing.
|
||||
* @param protocol - the current protocol.
|
||||
* @param sender - the sender.
|
||||
* @param packetId - the packet ID. Can be UNKNOWN_PACKET.
|
||||
* @param legacyId - the legacy packet ID. Can be UNKNOWN_PACKET.
|
||||
* @return The corresponding packet type.
|
||||
*/
|
||||
public static PacketType fromCurrent(Protocol protocol, Sender sender, int packetId, int legacyId) {
|
||||
PacketType type = getLookup().getFromCurrent(protocol, sender, packetId);
|
||||
|
||||
if (type == null) {
|
||||
type = new PacketType(protocol, sender, packetId, legacyId);
|
||||
|
||||
// Many may be scheduled, but only the first will be executed
|
||||
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a packet type from a packet class.
|
||||
* @param packetClass - the packet class.
|
||||
* @return The corresponding packet type, or NULL if not found.
|
||||
*/
|
||||
public static PacketType fromClass(Class<?> packetClass) {
|
||||
PacketType type = PacketRegistry.getPacketType(packetClass);
|
||||
|
||||
if (type != null)
|
||||
return type;
|
||||
throw new IllegalArgumentException("Class " + packetClass + " is not a registered packet.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every packet type with the given UPPER_CAMEL_CASE name.
|
||||
* <p>
|
||||
* Note that the collection is unmodiable.
|
||||
* @param name - the name.
|
||||
* @return Every packet type, or an empty collection.
|
||||
*/
|
||||
public static Collection<PacketType> fromName(String name) {
|
||||
return getLookup().getFromName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given class represents a packet class.
|
||||
* @param packetClass - the class to lookup.
|
||||
* @return TRUE if this is a packet class, FALSE otherwise.
|
||||
*/
|
||||
public static boolean hasClass(Class<?> packetClass) {
|
||||
return PacketRegistry.getPacketType(packetClass) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a particular packet type.
|
||||
* <p>
|
||||
* Note that the registration will be performed on the main thread.
|
||||
* @param type - the type to register.
|
||||
* @param name - the name of the packet.
|
||||
* @return A future telling us if our instance was registered.
|
||||
*/
|
||||
public static Future<Boolean> scheduleRegister(final PacketType type, final String name) {
|
||||
Callable<Boolean> callable = new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
ObjectEnum<PacketType> objEnum;
|
||||
|
||||
// A bit ugly, but performance is critical
|
||||
objEnum = getObjectEnum(type);
|
||||
|
||||
if (objEnum.registerMember(type, name)) {
|
||||
getLookup().addPacketTypes(Arrays.asList(type));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Execute in the main thread if possible
|
||||
if (Bukkit.getServer() == null || Application.isPrimaryThread()) {
|
||||
try {
|
||||
return Futures.immediateFuture(callable.call());
|
||||
} catch (Exception e) {
|
||||
return Futures.immediateFailedFuture(e);
|
||||
}
|
||||
}
|
||||
return ProtocolLibrary.getExecutorSync().submit(callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the correct object enum from a specific packet type.
|
||||
* @param type - the packet type.
|
||||
* @return The corresponding object enum.
|
||||
*/
|
||||
public static ObjectEnum<PacketType> getObjectEnum(final PacketType type) {
|
||||
switch (type.getProtocol()) {
|
||||
case HANDSHAKING:
|
||||
return type.isClient() ? Handshake.Client.getInstance() : Handshake.Server.getInstance();
|
||||
case PLAY:
|
||||
return type.isClient() ? Play.Client.getInstance() : Play.Server.getInstance();
|
||||
case STATUS:
|
||||
return type.isClient() ? Status.Client.getInstance() : Status.Server.getInstance();
|
||||
case LOGIN:
|
||||
return type.isClient() ? Login.Client.getInstance() : Login.Server.getInstance();
|
||||
case LEGACY:
|
||||
return type.isClient() ? Legacy.Client.getInstance() : Legacy.Server.getInstance();
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected protocol: " + type.getProtocol());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new packet type.
|
||||
* @param protocol - the current protocol.
|
||||
* @param sender - client or server.
|
||||
* @param currentId - the current packet ID, or
|
||||
* @param legacyId - the legacy packet ID.
|
||||
*/
|
||||
public PacketType(Protocol protocol, Sender sender, int currentId, int legacyId) {
|
||||
this(protocol, sender, currentId, legacyId, PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new packet type.
|
||||
* @param protocol - the current protocol.
|
||||
* @param sender - client or server.
|
||||
* @param currentId - the current packet ID.
|
||||
* @param legacyId - the legacy packet ID.
|
||||
* @param version - the version of the current ID.
|
||||
*/
|
||||
public PacketType(Protocol protocol, Sender sender, int currentId, int legacyId, MinecraftVersion version) {
|
||||
this.protocol = Preconditions.checkNotNull(protocol, "protocol cannot be NULL");
|
||||
this.sender = Preconditions.checkNotNull(sender, "sender cannot be NULL");
|
||||
this.currentId = currentId;
|
||||
this.legacyId = legacyId;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a legacy packet type.
|
||||
* @param sender - client or server.
|
||||
* @param legacyId - the legacy packet ID.
|
||||
*/
|
||||
public static PacketType newLegacy(Sender sender, int legacyId) {
|
||||
return new PacketType(Protocol.LEGACY, sender, PacketType.UNKNOWN_PACKET, legacyId, MinecraftVersion.WORLD_UPDATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this packet is supported on the current server.
|
||||
* @return Whether or not the packet is supported.
|
||||
*/
|
||||
public boolean isSupported() {
|
||||
return PacketRegistry.isSupported(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the protocol (the connection state) the packet type belongs.
|
||||
* @return The protocol of this type.
|
||||
*/
|
||||
public Protocol getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve which sender will transmit packets of this type.
|
||||
* @return The sender of these packets.
|
||||
*/
|
||||
public Sender getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this packet was sent by the client.
|
||||
* @return TRUE if it was, FALSE otherwise.
|
||||
*/
|
||||
public boolean isClient() {
|
||||
return sender == Sender.CLIENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this packet was sent by the server.
|
||||
* @return TRUE if it was, FALSE otherwise.
|
||||
*/
|
||||
public boolean isServer() {
|
||||
return sender == Sender.SERVER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current protocol ID for this packet type.
|
||||
* <p>
|
||||
* This is only unique within a specific protocol and target.
|
||||
* <p>
|
||||
* It is only unknown if the packet was removed in Minecraft 1.7.2.
|
||||
* @return The current ID, or {@link #UNKNOWN_PACKET} if unknown.
|
||||
*/
|
||||
public int getCurrentId() {
|
||||
return currentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the equivalent packet class.
|
||||
* @return The packet class, or NULL if not found.
|
||||
*/
|
||||
public Class<?> getPacketClass() {
|
||||
try {
|
||||
return PacketRegistry.getPacketClassFromType(this);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the declared enum name of this packet type.
|
||||
* @return The enum name.
|
||||
*/
|
||||
public String name() {
|
||||
return getObjectEnum(this).getDeclaredName(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Minecraft version for the current ID.
|
||||
* @return The Minecraft version.
|
||||
*/
|
||||
public MinecraftVersion getCurrentVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the legacy (1.6.4 or below) protocol ID of the packet type.
|
||||
* <p>
|
||||
* This ID is globally unique.
|
||||
* @return The legacy ID, or {@link #UNKNOWN_PACKET} if unknown.
|
||||
*/
|
||||
public int getLegacyId() {
|
||||
return legacyId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(protocol, sender, currentId, legacyId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this)
|
||||
return true;
|
||||
|
||||
if (obj instanceof PacketType) {
|
||||
PacketType other = (PacketType) obj;
|
||||
return protocol == other.protocol &&
|
||||
sender == other.sender &&
|
||||
currentId == other.currentId &&
|
||||
legacyId == other.legacyId;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PacketType other) {
|
||||
return ComparisonChain.start().
|
||||
compare(protocol, other.getProtocol()).
|
||||
compare(sender, other.getSender()).
|
||||
compare(currentId, other.getCurrentId()).
|
||||
compare(legacyId, other.getLegacyId()).
|
||||
result();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Class<?> clazz = getPacketClass();;
|
||||
|
||||
if (clazz == null)
|
||||
return "UNREGISTERED [" + protocol + ", " + sender + ", " + currentId + ", legacy: " + legacyId + "]";
|
||||
else
|
||||
return clazz.getSimpleName() + "[" + currentId + ", legacy: " + legacyId + "]";
|
||||
}
|
||||
}
|
||||
+27
-65
@@ -2,8 +2,6 @@ package com.comphenix.protocol;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
@@ -17,16 +15,16 @@ import com.google.common.collect.Multimap;
|
||||
* @author Kristian
|
||||
*/
|
||||
class PacketTypeLookup {
|
||||
public static class ProtocolSenderLookup {
|
||||
private static class ProtocolSenderLookup {
|
||||
// Unroll lookup for performance reasons
|
||||
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<>();
|
||||
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();
|
||||
|
||||
/**
|
||||
* Retrieve the correct integer map for a specific protocol and sender.
|
||||
@@ -49,54 +47,18 @@ class PacketTypeLookup {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClassLookup {
|
||||
// Unroll lookup for performance reasons
|
||||
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.
|
||||
* @param protocol - the protocol.
|
||||
* @param sender - the sender.
|
||||
* @return The integer map of packets.
|
||||
*/
|
||||
public Map<String, PacketType> getMap(Protocol protocol, Sender sender) {
|
||||
switch (protocol) {
|
||||
case HANDSHAKING:
|
||||
return sender == Sender.CLIENT ? HANDSHAKE_CLIENT : HANDSHAKE_SERVER;
|
||||
case PLAY:
|
||||
return sender == Sender.CLIENT ? GAME_CLIENT : GAME_SERVER;
|
||||
case STATUS:
|
||||
return sender == Sender.CLIENT ? STATUS_CLIENT : STATUS_SERVER;
|
||||
case LOGIN:
|
||||
return sender == Sender.CLIENT ? LOGIN_CLIENT : LOGIN_SERVER;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unable to find protocol " + protocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Packet IDs from 1.6.4 and below
|
||||
private final IntegerMap<PacketType> legacyLookup = new IntegerMap<>();
|
||||
private final IntegerMap<PacketType> serverLookup = new IntegerMap<>();
|
||||
private final IntegerMap<PacketType> clientLookup = new IntegerMap<>();
|
||||
private final IntegerMap<PacketType> legacyLookup = new IntegerMap<PacketType>();
|
||||
private final IntegerMap<PacketType> serverLookup = new IntegerMap<PacketType>();
|
||||
private final IntegerMap<PacketType> clientLookup = new IntegerMap<PacketType>();
|
||||
|
||||
// Packets for 1.7.2
|
||||
private final ProtocolSenderLookup idLookup = new ProtocolSenderLookup();
|
||||
|
||||
// Packets for 1.8+
|
||||
private final ClassLookup classLookup = new ClassLookup();
|
||||
private final ProtocolSenderLookup currentLookup = new ProtocolSenderLookup();
|
||||
|
||||
// Packets based on name
|
||||
private final Multimap<String, PacketType> nameLookup = HashMultimap.create();
|
||||
|
||||
|
||||
/**
|
||||
* Add a collection of packet types to the lookup.
|
||||
* @param types - the types to add.
|
||||
@@ -105,11 +67,21 @@ class PacketTypeLookup {
|
||||
Preconditions.checkNotNull(types, "types cannot be NULL");
|
||||
|
||||
for (PacketType type : types) {
|
||||
int legacy = type.getLegacyId();
|
||||
|
||||
// Skip unknown legacy packets
|
||||
if (legacy != PacketType.UNKNOWN_PACKET) {
|
||||
if (type.isServer())
|
||||
serverLookup.put(type.getLegacyId(), type);
|
||||
if (type.isClient())
|
||||
clientLookup.put(type.getLegacyId(), type);
|
||||
legacyLookup.put(type.getLegacyId(), type);
|
||||
}
|
||||
// Skip unknown current packets
|
||||
if (type.getCurrentId() != PacketType.UNKNOWN_PACKET) {
|
||||
idLookup.getMap(type.getProtocol(), type.getSender()).put(type.getCurrentId(), type);
|
||||
classLookup.getMap(type.getProtocol(), type.getSender()).put(type.getClassNames().get(0), type);
|
||||
currentLookup.getMap(type.getProtocol(), type.getSender()).put(type.getCurrentId(), type);
|
||||
}
|
||||
// Save name
|
||||
nameLookup.put(type.name(), type);
|
||||
}
|
||||
return this;
|
||||
@@ -160,18 +132,8 @@ class PacketTypeLookup {
|
||||
* @param sender - the sender.
|
||||
* @param packetId - the packet ID.
|
||||
* @return The corresponding packet type, or NULL if not found.
|
||||
* @deprecated IDs are no longer reliable
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketType getFromCurrent(Protocol protocol, Sender sender, int packetId) {
|
||||
return idLookup.getMap(protocol, sender).get(packetId);
|
||||
}
|
||||
|
||||
public PacketType getFromCurrent(Protocol protocol, Sender sender, String name) {
|
||||
return classLookup.getMap(protocol, sender).get(name);
|
||||
}
|
||||
|
||||
public ClassLookup getClassLookup() {
|
||||
return classLookup;
|
||||
return currentLookup.getMap(protocol, sender).get(packetId);
|
||||
}
|
||||
}
|
||||
+31
-32
@@ -1,29 +1,28 @@
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
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.DiscreteDomains;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
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;
|
||||
import com.google.common.collect.Ranges;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
class PacketTypeParser {
|
||||
public static final Range<Integer> DEFAULT_MAX_RANGE = Range.closed(0, 255);
|
||||
public final static Range<Integer> DEFAULT_MAX_RANGE = Ranges.closed(0, 255);
|
||||
|
||||
private Sender side = null;
|
||||
private Protocol protocol = null;
|
||||
|
||||
public Set<PacketType> parseTypes(Deque<String> arguments, Range<Integer> defaultRange) {
|
||||
final Set<PacketType> result = new HashSet<>();
|
||||
Set<PacketType> result = Sets.newHashSet();
|
||||
side = null;
|
||||
protocol = null;
|
||||
|
||||
@@ -54,7 +53,7 @@ class PacketTypeParser {
|
||||
// And finally, parse packet names if we have a protocol
|
||||
if (protocol != null) {
|
||||
for (Iterator<String> it = arguments.iterator(); it.hasNext(); ) {
|
||||
String name = it.next().toUpperCase(Locale.ENGLISH);
|
||||
String name = it.next().toUpperCase();
|
||||
Collection<PacketType> names = PacketType.fromName(name);
|
||||
|
||||
for (PacketType type : names) {
|
||||
@@ -68,11 +67,12 @@ class PacketTypeParser {
|
||||
|
||||
// Supply a default integer range
|
||||
if (ranges.isEmpty() && result.isEmpty()) {
|
||||
ranges = Collections.singletonList(defaultRange);
|
||||
ranges = Lists.newArrayList();
|
||||
ranges.add(defaultRange);
|
||||
}
|
||||
|
||||
for (Range<Integer> range : ranges) {
|
||||
for (Integer id : ContiguousSet.create(range, DiscreteDomain.integers())) {
|
||||
for (Integer id : range.asSet(DiscreteDomains.integers())) {
|
||||
// Deprecated packets
|
||||
if (protocol == null) {
|
||||
if (PacketType.hasLegacy(id)) {
|
||||
@@ -129,20 +129,19 @@ class PacketTypeParser {
|
||||
* @return The protocol, or NULL if not found.
|
||||
*/
|
||||
public Protocol parseProtocol(String text) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.IntEnum;
|
||||
|
||||
/**
|
||||
* List of known packet IDs since 1.3.2.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @author Kristian
|
||||
*/
|
||||
@Deprecated
|
||||
public final class Packets {
|
||||
|
||||
/**
|
||||
* The highest possible packet ID. It's unlikely that this value will ever change.
|
||||
*/
|
||||
public static final int MAXIMUM_PACKET_ID = 255;
|
||||
|
||||
/**
|
||||
* The maximum number of unique packet IDs. It's unlikely this will ever change.
|
||||
*/
|
||||
public static final int PACKET_COUNT = 256;
|
||||
|
||||
/**
|
||||
* List of packets sent only by the server.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @author Kristian
|
||||
*/
|
||||
@Deprecated
|
||||
public static final class Server extends IntEnum {
|
||||
/**
|
||||
* The singleton instance. Can also be retrieved from the parent class.
|
||||
*/
|
||||
private static Server INSTANCE = new Server();
|
||||
|
||||
public static final int KEEP_ALIVE = 0;
|
||||
public static final int LOGIN = 1;
|
||||
public static final int CHAT = 3;
|
||||
public static final int UPDATE_TIME = 4;
|
||||
public static final int ENTITY_EQUIPMENT = 5;
|
||||
public static final int SPAWN_POSITION = 6;
|
||||
public static final int UPDATE_HEALTH = 8;
|
||||
public static final int RESPAWN = 9;
|
||||
public static final int FLYING = 10;
|
||||
public static final int PLAYER_POSITION = 11;
|
||||
public static final int PLAYER_LOOK = 12;
|
||||
public static final int PLAYER_LOOK_MOVE = 13;
|
||||
/**
|
||||
* Made bi-directional in 1.4.6.
|
||||
*/
|
||||
public static final int BLOCK_ITEM_SWITCH = 16;
|
||||
public static final int ENTITY_LOCATION_ACTION = 17;
|
||||
public static final int ARM_ANIMATION = 18;
|
||||
public static final int NAMED_ENTITY_SPAWN = 20;
|
||||
/**
|
||||
* Removed in 1.4.6 and replaced with VEHICLE_SPAWN.
|
||||
* @see <a href="http://www.wiki.vg/Protocol_History#2012-12-20">Protocol History - MinecraftCoalition</a>
|
||||
*/
|
||||
@Deprecated()
|
||||
public static final int PICKUP_SPAWN = 21;
|
||||
public static final int COLLECT = 22;
|
||||
public static final int VEHICLE_SPAWN = 23;
|
||||
public static final int MOB_SPAWN = 24;
|
||||
public static final int ENTITY_PAINTING = 25;
|
||||
public static final int ADD_EXP_ORB = 26;
|
||||
public static final int ENTITY_VELOCITY = 28;
|
||||
public static final int DESTROY_ENTITY = 29;
|
||||
public static final int ENTITY = 30;
|
||||
public static final int REL_ENTITY_MOVE = 31;
|
||||
public static final int ENTITY_LOOK = 32;
|
||||
public static final int REL_ENTITY_MOVE_LOOK = 33;
|
||||
public static final int ENTITY_TELEPORT = 34;
|
||||
public static final int ENTITY_HEAD_ROTATION = 35;
|
||||
public static final int ENTITY_STATUS = 38;
|
||||
public static final int ATTACH_ENTITY = 39;
|
||||
|
||||
/**
|
||||
* Sent when an entities DataWatcher is updated.
|
||||
* <p>
|
||||
* Remember to clone the packet if you are modifying it.
|
||||
*/
|
||||
public static final int ENTITY_METADATA = 40;
|
||||
public static final int MOB_EFFECT = 41;
|
||||
public static final int REMOVE_MOB_EFFECT = 42;
|
||||
public static final int SET_EXPERIENCE = 43;
|
||||
public static final int UPDATE_ATTRIBUTES = 44;
|
||||
public static final int MAP_CHUNK = 51;
|
||||
public static final int MULTI_BLOCK_CHANGE = 52;
|
||||
public static final int BLOCK_CHANGE = 53;
|
||||
public static final int PLAY_NOTE_BLOCK = 54;
|
||||
public static final int BLOCK_BREAK_ANIMATION = 55;
|
||||
public static final int MAP_CHUNK_BULK = 56;
|
||||
public static final int EXPLOSION = 60;
|
||||
public static final int WORLD_EVENT = 61;
|
||||
public static final int NAMED_SOUND_EFFECT = 62;
|
||||
public static final int WORLD_PARTICLES = 63;
|
||||
public static final int BED = 70;
|
||||
public static final int WEATHER = 71;
|
||||
public static final int OPEN_WINDOW = 100;
|
||||
public static final int CLOSE_WINDOW = 101;
|
||||
public static final int SET_SLOT = 103;
|
||||
public static final int WINDOW_ITEMS = 104;
|
||||
public static final int CRAFT_PROGRESS_BAR = 105;
|
||||
public static final int TRANSACTION = 106;
|
||||
public static final int SET_CREATIVE_SLOT = 107;
|
||||
public static final int UPDATE_SIGN = 130;
|
||||
public static final int ITEM_DATA = 131;
|
||||
|
||||
/**
|
||||
* Sent the first time a tile entity (chest inventory, etc.) is withing range of the player, or has been updated.
|
||||
* <p>
|
||||
* Remember to clone the packet if you are modifying it.
|
||||
*/
|
||||
public static final int TILE_ENTITY_DATA = 132;
|
||||
public static final int OPEN_TILE_ENTITY = 133;
|
||||
public static final int STATISTIC = 200;
|
||||
public static final int PLAYER_INFO = 201;
|
||||
public static final int ABILITIES = 202;
|
||||
public static final int TAB_COMPLETE = 203;
|
||||
public static final int SCOREBOARD_OBJECTIVE = 206;
|
||||
public static final int UPDATE_SCORE = 207;
|
||||
public static final int DISPLAY_SCOREBOARD = 208;
|
||||
public static final int TEAMS = 209;
|
||||
public static final int CUSTOM_PAYLOAD = 250;
|
||||
public static final int KEY_RESPONSE = 252;
|
||||
public static final int KEY_REQUEST = 253;
|
||||
public static final int KICK_DISCONNECT = 255;
|
||||
|
||||
/**
|
||||
* This packet was introduced in 1.7.2.
|
||||
*/
|
||||
public static final int PING_TIME = 230;
|
||||
|
||||
/**
|
||||
* This packet was introduced in 1.7.2.
|
||||
*/
|
||||
public static final int LOGIN_SUCCESS = 232;
|
||||
|
||||
/**
|
||||
* A registry that parses between names and packet IDs.
|
||||
* @return The current server registry.
|
||||
*/
|
||||
public static Server getRegistry() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given packet is a valid server packet in the current version of Minecraft.
|
||||
* <p>
|
||||
* Use {@link PacketType#isSupported()} instead.
|
||||
* @param packetID - the packet to test.
|
||||
* @return TRUE if this packet is supported, FALSE otherwise.
|
||||
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isSupported(int packetID) throws FieldAccessException {
|
||||
return PacketFilterManager.getServerPackets().contains(packetID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every client packet the current version of Minecraft is aware of.
|
||||
* @return Every supported server packet.
|
||||
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Set<Integer> getSupported() throws FieldAccessException {
|
||||
return PacketFilterManager.getServerPackets();
|
||||
}
|
||||
|
||||
// We only allow a single instance of this class
|
||||
private Server() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of packets sent by the client.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @author Kristian
|
||||
*/
|
||||
@Deprecated
|
||||
public static class Client extends IntEnum {
|
||||
/**
|
||||
* The singleton instance. Can also be retrieved from the parent class.
|
||||
*/
|
||||
private static Client INSTANCE = new Client();
|
||||
|
||||
public static final int KEEP_ALIVE = 0;
|
||||
public static final int LOGIN = 1;
|
||||
public static final int HANDSHAKE = 2;
|
||||
public static final int CHAT = 3;
|
||||
public static final int USE_ENTITY = 7;
|
||||
|
||||
/**
|
||||
* Since 1.3.1, the client no longer sends a respawn packet. Moved to CLIENT_COMMAND.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int RESPAWN = 9;
|
||||
|
||||
public static final int FLYING = 10;
|
||||
public static final int PLAYER_POSITION = 11;
|
||||
public static final int PLAYER_LOOK = 12;
|
||||
public static final int PLAYER_LOOK_MOVE = 13;
|
||||
public static final int BLOCK_DIG = 14;
|
||||
public static final int PLACE = 15;
|
||||
public static final int BLOCK_ITEM_SWITCH = 16;
|
||||
public static final int ARM_ANIMATION = 18;
|
||||
public static final int ENTITY_ACTION = 19;
|
||||
public static final int PLAYER_INPUT = 27;
|
||||
public static final int CLOSE_WINDOW = 101;
|
||||
public static final int WINDOW_CLICK = 102;
|
||||
public static final int TRANSACTION = 106;
|
||||
public static final int SET_CREATIVE_SLOT = 107;
|
||||
public static final int BUTTON_CLICK = 108;
|
||||
public static final int UPDATE_SIGN = 130;
|
||||
public static final int ABILITIES = 202;
|
||||
public static final int TAB_COMPLETE = 203;
|
||||
public static final int LOCALE_AND_VIEW_DISTANCE = 204;
|
||||
public static final int CLIENT_COMMAND = 205;
|
||||
public static final int CUSTOM_PAYLOAD = 250;
|
||||
public static final int KEY_RESPONSE = 252;
|
||||
public static final int GET_INFO = 254;
|
||||
public static final int KICK_DISCONNECT = 255;
|
||||
|
||||
/**
|
||||
* This packet was introduced in 1.7.2.
|
||||
*/
|
||||
public static final int PING_TIME = 230;
|
||||
|
||||
/**
|
||||
* This packet was introduced in 1.7.2.
|
||||
*/
|
||||
public static final int LOGIN_START = 231;
|
||||
|
||||
/**
|
||||
* A registry that parses between names and packet IDs.
|
||||
* @return The current client registry.
|
||||
*/
|
||||
public static Client getRegistry() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given packet is a valid client packet in the current version of Minecraft.
|
||||
* @param packetID - the packet to test.
|
||||
* @return TRUE if this packet is supported, FALSE otherwise.
|
||||
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
|
||||
*/
|
||||
public static boolean isSupported(int packetID) throws FieldAccessException {
|
||||
return PacketFilterManager.getClientPackets().contains(packetID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every client packet the current version of Minecraft is aware of.
|
||||
* @return Every supported client packet.
|
||||
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
|
||||
*/
|
||||
public static Set<Integer> getSupported() throws FieldAccessException {
|
||||
return PacketFilterManager.getClientPackets();
|
||||
}
|
||||
|
||||
// Like above
|
||||
private Client() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A registry that parses between names and packet IDs.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @return The current client registry.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Server getServerRegistry() {
|
||||
return Server.getRegistry();
|
||||
}
|
||||
|
||||
/**
|
||||
* A registry that parses between names and packet IDs.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @return The current server registry.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Client getClientRegistry() {
|
||||
return Client.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a packet by name. Must be capitalized and use underscores.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @param name - name of packet to find.
|
||||
* @return The packet ID found.
|
||||
*/
|
||||
@Deprecated
|
||||
public static int valueOf(String name) {
|
||||
Integer serverAttempt = Server.INSTANCE.valueOf(name);
|
||||
|
||||
if (serverAttempt != null)
|
||||
return serverAttempt;
|
||||
else
|
||||
return Client.INSTANCE.valueOf(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the name of a packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @param packetID - packet to retrieve name.
|
||||
* @return The name, or NULL if unable to find such a packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getDeclaredName(int packetID) {
|
||||
String serverAttempt = Server.INSTANCE.getDeclaredName(packetID);
|
||||
|
||||
if (serverAttempt != null)
|
||||
return serverAttempt;
|
||||
else
|
||||
return Client.INSTANCE.getDeclaredName(packetID);
|
||||
}
|
||||
}
|
||||
+214
-131
@@ -1,81 +1,90 @@
|
||||
/**
|
||||
* 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
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.io.Files;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.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;
|
||||
|
||||
/**
|
||||
* 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";
|
||||
private static final String SECTION_AUTOUPDATER = "auto updater";
|
||||
|
||||
|
||||
private static final String METRICS_ENABLED = "metrics";
|
||||
|
||||
private static final String IGNORE_VERSION_CHECK = "ignore version check";
|
||||
|
||||
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 CHAT_WARNINGS = "chat warnings";
|
||||
|
||||
private static final String INJECTION_METHOD = "injection method";
|
||||
|
||||
private static final String SCRIPT_ENGINE_NAME = "script engine";
|
||||
private static final String SUPPRESSED_REPORTS = "suppressed reports";
|
||||
|
||||
|
||||
private static final String UPDATER_NOTIFY = "notify";
|
||||
private static final String UPDATER_DOWNLAD = "download";
|
||||
private static final String UPDATER_DELAY = "delay";
|
||||
|
||||
|
||||
// Defaults
|
||||
private static final long DEFAULT_UPDATER_DELAY = 43200;
|
||||
|
||||
|
||||
private Plugin plugin;
|
||||
private Configuration config;
|
||||
private boolean loadingSections;
|
||||
|
||||
|
||||
private ConfigurationSection global;
|
||||
private ConfigurationSection updater;
|
||||
|
||||
|
||||
// Last update time
|
||||
private long lastUpdateTime;
|
||||
private boolean configChanged;
|
||||
private boolean valuesChanged;
|
||||
|
||||
|
||||
// Modifications
|
||||
private int modCount;
|
||||
|
||||
|
||||
public ProtocolConfig(Plugin plugin) {
|
||||
this(plugin, plugin.getConfig());
|
||||
}
|
||||
|
||||
public ProtocolConfig(Plugin plugin, Configuration config) {
|
||||
this.plugin = plugin;
|
||||
reloadConfig();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reload configuration file.
|
||||
*/
|
||||
@@ -84,23 +93,22 @@ public class ProtocolConfig {
|
||||
configChanged = false;
|
||||
valuesChanged = false;
|
||||
modCount++;
|
||||
|
||||
|
||||
this.config = plugin.getConfig();
|
||||
this.lastUpdateTime = loadLastUpdate();
|
||||
loadSections(!loadingSections);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the last update time stamp from the file system.
|
||||
*
|
||||
* @return Last update time stamp.
|
||||
*/
|
||||
private long loadLastUpdate() {
|
||||
File dataFile = getLastUpdateFile();
|
||||
|
||||
|
||||
if (dataFile.exists()) {
|
||||
try {
|
||||
return Long.parseLong(Files.toString(dataFile, StandardCharsets.UTF_8));
|
||||
return Long.parseLong(Files.toString(dataFile, Charsets.UTF_8));
|
||||
} catch (NumberFormatException e) {
|
||||
plugin.getLogger().warning("Cannot parse " + dataFile + " as a number.");
|
||||
} catch (IOException e) {
|
||||
@@ -110,41 +118,37 @@ public class ProtocolConfig {
|
||||
// Default last update
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store the given time stamp.
|
||||
*
|
||||
* @param value - time stamp to store.
|
||||
*/
|
||||
private void saveLastUpdate(long value) {
|
||||
File dataFile = getLastUpdateFile();
|
||||
|
||||
|
||||
// The data folder must exist
|
||||
dataFile.getParentFile().mkdirs();
|
||||
|
||||
if (dataFile.exists()) {
|
||||
|
||||
if (dataFile.exists())
|
||||
dataFile.delete();
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
Files.write(Long.toString(value), dataFile, StandardCharsets.UTF_8);
|
||||
Files.write(Long.toString(value), dataFile, Charsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot write " + dataFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the file that is used to store the update time stamp.
|
||||
*
|
||||
* @return File storing the update time stamp.
|
||||
*/
|
||||
private File getLastUpdateFile() {
|
||||
return new File(plugin.getDataFolder(), LAST_UPDATE_FILE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load data sections.
|
||||
*
|
||||
* @param copyDefaults - whether or not to copy configuration defaults.
|
||||
*/
|
||||
private void loadSections(boolean copyDefaults) {
|
||||
@@ -153,30 +157,25 @@ public class ProtocolConfig {
|
||||
}
|
||||
if (global != null) {
|
||||
updater = global.getConfigurationSection(SECTION_AUTOUPDATER);
|
||||
if (updater.getValues(true).isEmpty()) {
|
||||
plugin.getLogger().warning("Updater section is missing, regenerate your config!");
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically copy defaults
|
||||
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;
|
||||
|
||||
loadingSections = false;
|
||||
|
||||
// Inform the user
|
||||
plugin.getLogger().info("Created default configuration.");
|
||||
System.out.println("[ProtocolLib] Created default configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -186,129 +185,185 @@ public class ProtocolConfig {
|
||||
section.set(path, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getGlobalValue(String path, T def) {
|
||||
try {
|
||||
return (T) global.get(path, def);
|
||||
} catch (Throwable ex) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getUpdaterValue(String path, T def) {
|
||||
try {
|
||||
return (T) updater.get(path, def);
|
||||
} catch (Throwable ex) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a reference to the configuration file.
|
||||
*
|
||||
* @return Configuration file on disk.
|
||||
*/
|
||||
public File getFile() {
|
||||
return new File(plugin.getDataFolder(), "config.yml");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if detailed error reporting is enabled. Default FALSE.
|
||||
*
|
||||
* @return TRUE if it is enabled, FALSE otherwise.
|
||||
*/
|
||||
public boolean isDetailedErrorReporting() {
|
||||
return getGlobalValue(DETAILED_ERROR, false);
|
||||
return global.getBoolean(DETAILED_ERROR, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Print warnings to players with protocol.info
|
||||
* @return true if enabled, false if not
|
||||
* Set whether or not detailed error reporting is enabled.
|
||||
* @param value - TRUE if it is enabled, FALSE otherwise.
|
||||
*/
|
||||
public boolean isChatWarnings() {
|
||||
return getGlobalValue(CHAT_WARNINGS, true);
|
||||
public void setDetailedErrorReporting(boolean value) {
|
||||
global.set(DETAILED_ERROR, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
return updater.getBoolean(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);
|
||||
return updater != null && updater.getBoolean(UPDATER_DOWNLAD, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return getGlobalValue(DEBUG_MODE_ENABLED, false);
|
||||
return global.getBoolean(DEBUG_MODE_ENABLED, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set whether or not debug mode is enabled.
|
||||
*
|
||||
* @param value - TRUE if it is enabled, FALSE otherwise.
|
||||
*/
|
||||
public void setDebug(boolean value) {
|
||||
setConfig(global, DEBUG_MODE_ENABLED, value);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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>()));
|
||||
return ImmutableList.copyOf(global.getStringList(SUPPRESSED_REPORTS));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
// Note that the delay must be greater than 59 seconds
|
||||
return Math.max(getUpdaterValue(UPDATER_DELAY, 0), DEFAULT_UPDATER_DELAY);
|
||||
return Math.max(updater.getInt(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, "");
|
||||
return global.getString(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);
|
||||
return global.getBoolean(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 global.getBoolean(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() {
|
||||
@@ -318,56 +373,84 @@ 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.
|
||||
*
|
||||
* 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) {
|
||||
public void setAutoLastTime(long lastTimeSeconds) {
|
||||
this.valuesChanged = true;
|
||||
this.lastUpdateTime = lastTimeSeconds;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the unique name of the script engine to use for filtering.
|
||||
*
|
||||
* @return Script engine to use.
|
||||
*/
|
||||
public String getScriptEngineName() {
|
||||
return getGlobalValue(SCRIPT_ENGINE_NAME, "JavaScript");
|
||||
return global.getString(SCRIPT_ENGINE_NAME, "JavaScript");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
setConfig(global, SCRIPT_ENGINE_NAME, name);
|
||||
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().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() {
|
||||
return modCount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
configChanged = false;
|
||||
@@ -0,0 +1,655 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
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 org.bukkit.Server;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import com.comphenix.executors.BukkitExecutors;
|
||||
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.DelayedSingleTask;
|
||||
import com.comphenix.protocol.injector.InternalManager;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.metrics.Statistics;
|
||||
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||
import com.comphenix.protocol.utility.ChatExtensions;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
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 com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* The main entry point for ProtocolLib.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ProtocolLibrary 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_PLUGIN_LOAD_ERROR = new ReportType("Cannot load ProtocolLib.");
|
||||
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_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_UPDATE_PLUGIN = new ReportType("Cannot perform automatic updates.");
|
||||
|
||||
/**
|
||||
* The minimum version ProtocolLib has been tested with.
|
||||
*/
|
||||
public static final String MINIMUM_MINECRAFT_VERSION = "1.0.0";
|
||||
|
||||
/**
|
||||
* The maximum version ProtocolLib has been tested with,
|
||||
*/
|
||||
public static final String MAXIMUM_MINECRAFT_VERSION = "1.7.10";
|
||||
|
||||
/**
|
||||
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version was released.
|
||||
*/
|
||||
public static final String MINECRAFT_LAST_RELEASE_DATE = "2013-12-10";
|
||||
|
||||
// Different commands
|
||||
private enum ProtocolCommand {
|
||||
FILTER,
|
||||
PACKET,
|
||||
PROTOCOL
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of milliseconds per second.
|
||||
*/
|
||||
static final long MILLI_PER_SECOND = 1000;
|
||||
|
||||
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
|
||||
private static ProtocolConfig config;
|
||||
|
||||
// Metrics and statistics
|
||||
private Statistics statistics;
|
||||
|
||||
// Executors
|
||||
private static ListeningScheduledExecutorService executorAsync;
|
||||
private static ListeningScheduledExecutorService executorSync;
|
||||
|
||||
// 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;
|
||||
|
||||
// Logger
|
||||
private Logger logger;
|
||||
private Handler redirectHandler;
|
||||
|
||||
// Commands
|
||||
private CommandProtocol commandProtocol;
|
||||
private CommandPacket commandPacket;
|
||||
private CommandFilter commandFilter;
|
||||
|
||||
// Whether or not disable is not needed
|
||||
private boolean skipDisable;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Load configuration
|
||||
logger = getLoggerSafely();
|
||||
Application.registerPrimaryThread();
|
||||
|
||||
// Initialize enhancer factory
|
||||
EnhancerFactory.getInstance().setClassLoader(getClassLoader());
|
||||
|
||||
// Initialize executors
|
||||
executorAsync = BukkitExecutors.newAsynchronous(this);
|
||||
executorSync = BukkitExecutors.newSynchronous(this);
|
||||
|
||||
// Add global parameters
|
||||
DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this);
|
||||
reporter = getFilteredReporter(detailedReporter);
|
||||
|
||||
try {
|
||||
config = new ProtocolConfig(this);
|
||||
} catch (Exception e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_LOAD_CONFIG).error(e));
|
||||
|
||||
// Load it again
|
||||
if (deleteConfig()) {
|
||||
config = new ProtocolConfig(this);
|
||||
} else {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_DELETE_CONFIG));
|
||||
}
|
||||
}
|
||||
|
||||
// Print the state of the debug mode
|
||||
if (config.isDebug()) {
|
||||
logger.warning("Debug mode is enabled!");
|
||||
}
|
||||
// And the state of the error reporter
|
||||
if (config.isDetailedErrorReporting()) {
|
||||
detailedReporter.setDetailedReporting(true);
|
||||
logger.warning("Detailed error reporting enabled!");
|
||||
}
|
||||
|
||||
try {
|
||||
// Check for other versions
|
||||
checkConflictingVersions();
|
||||
|
||||
// Handle unexpected Minecraft versions
|
||||
MinecraftVersion version = verifyMinecraftVersion();
|
||||
|
||||
unhookTask = new DelayedSingleTask(this);
|
||||
protocolManager = PacketFilterManager.newBuilder().classLoader(getClassLoader()).server(getServer()).library(this).minecraftVersion(version).unhookTask(unhookTask).reporter(reporter).build();
|
||||
|
||||
// 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);
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_PLUGIN_LOAD_ERROR).error(e).callerParam(protocolManager));
|
||||
disablePlugin();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all command handlers.
|
||||
*/
|
||||
private void initializeCommands() {
|
||||
// Initialize command handlers
|
||||
for (ProtocolCommand command : ProtocolCommand.values()) {
|
||||
try {
|
||||
switch (command) {
|
||||
case PROTOCOL:
|
||||
commandProtocol = new CommandProtocol(reporter, this, config);
|
||||
break;
|
||||
case FILTER:
|
||||
commandFilter = new CommandFilter(reporter, this, config);
|
||||
break;
|
||||
case PACKET:
|
||||
commandPacket = new CommandPacket(reporter, this, logger, commandFilter, protocolManager);
|
||||
break;
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_REGISTER_COMMAND).messageParam(command.name(), e.getMessage()).error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
@Override
|
||||
protected Report filterReport(Object sender, Report report, boolean detailed) {
|
||||
try {
|
||||
String canonicalName = ReportType.getReportName(sender, report.getType());
|
||||
String reportName = Iterables.getLast(Splitter.on("#").split(canonicalName)).toUpperCase();
|
||||
|
||||
if (config != null && config.getModificationCount() != lastModCount) {
|
||||
// Update our cached set again
|
||||
reports = Sets.newHashSet(config.getSuppressedReports());
|
||||
lastModCount = config.getModificationCount();
|
||||
}
|
||||
|
||||
// Cancel reports either on the full canonical name, or just the report name
|
||||
if (reports.contains(canonicalName) || reports.contains(reportName))
|
||||
return null;
|
||||
|
||||
} catch (Exception e) {
|
||||
// Only report this with a minor message
|
||||
logger.warning("Error filtering reports: " + e.toString());
|
||||
}
|
||||
// Don't filter anything
|
||||
return report;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean deleteConfig() {
|
||||
return config.getFile().delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
super.reloadConfig();
|
||||
|
||||
// Reload configuration
|
||||
if (config != null) {
|
||||
config.reloadConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupBroadcastUsers(final String permission) {
|
||||
// Guard against multiple calls
|
||||
if (redirectHandler != null)
|
||||
return;
|
||||
|
||||
// Broadcast information to every user too
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
// Not needed.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
// Do nothing.
|
||||
}
|
||||
};
|
||||
|
||||
logger.addHandler(redirectHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
try {
|
||||
Server server = 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[] { " PROTOCOLLIB DOES NOT SUPPORT PLUGIN RELOADERS. ", " PLEASE USE THE BUILT-IN RELOAD COMMAND. ", };
|
||||
|
||||
// Print as severe
|
||||
for (String line : ChatExtensions.toFlowerBox(message, "*", 3, 1)) {
|
||||
directLogging.severe(line);
|
||||
}
|
||||
disablePlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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.");
|
||||
}
|
||||
|
||||
// Set up command handlers
|
||||
registerCommand(CommandProtocol.NAME, commandProtocol);
|
||||
registerCommand(CommandPacket.NAME, commandPacket);
|
||||
registerCommand(CommandFilter.NAME, commandFilter);
|
||||
|
||||
// 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);
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_PLUGIN_ENABLE_ERROR).error(e));
|
||||
disablePlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to enable statistics
|
||||
try {
|
||||
if (config.isMetricsEnabled()) {
|
||||
statistics = new Statistics(this);
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_METRICS_IO_ERROR).error(e).callerParam(statistics));
|
||||
} catch (Throwable e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_METRICS_GENERIC_ERROR).error(e).callerParam(statistics));
|
||||
}
|
||||
}
|
||||
|
||||
// Used to check Minecraft version
|
||||
private MinecraftVersion verifyMinecraftVersion() {
|
||||
MinecraftVersion minimum = new MinecraftVersion(MINIMUM_MINECRAFT_VERSION);
|
||||
MinecraftVersion maximum = new MinecraftVersion(MAXIMUM_MINECRAFT_VERSION);
|
||||
|
||||
try {
|
||||
MinecraftVersion current = new MinecraftVersion(getServer());
|
||||
|
||||
// Skip certain versions
|
||||
if (!config.getIgnoreVersionCheck().equals(current.getVersion())) {
|
||||
// We'll just warn the user for now
|
||||
if (current.compareTo(minimum) < 0)
|
||||
logger.warning("Version " + current + " is lower than the minimum " + minimum);
|
||||
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));
|
||||
|
||||
// Unknown version - just assume it is the latest
|
||||
return maximum;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkConflictingVersions() {
|
||||
Pattern ourPlugin = Pattern.compile("ProtocolLib-(.*)\\.jar");
|
||||
MinecraftVersion currentVersion = new MinecraftVersion(this.getDescription().getVersion());
|
||||
MinecraftVersion newestVersion = null;
|
||||
|
||||
// Skip the file that contains this current instance however
|
||||
File loadedFile = getFile();
|
||||
|
||||
try {
|
||||
// Scan the plugin folder for newer versions of ProtocolLib
|
||||
File pluginFolder = new File("plugins/");
|
||||
|
||||
for (File candidate : pluginFolder.listFiles()) {
|
||||
if (candidate.isFile() && !candidate.equals(loadedFile)) {
|
||||
Matcher match = ourPlugin.matcher(candidate.getName());
|
||||
|
||||
if (match.matches()) {
|
||||
MinecraftVersion version = new MinecraftVersion(match.group(1));
|
||||
|
||||
if (candidate.length() == 0) {
|
||||
// Delete and inform the user
|
||||
logger.info((candidate.delete() ? "Deleted " : "Could not delete ") + candidate);
|
||||
} else if (newestVersion == null || newestVersion.compareTo(version) < 0) {
|
||||
newestVersion = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_DETECT_CONFLICTING_PLUGINS).error(e));
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
throw new IllegalStateException(String.format("Detected a newer version of ProtocolLib (%s) in plugin folder than the current (%s). Disabling.", newestVersion.getVersion(), currentVersion.getVersion()));
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCommand(String name, CommandExecutor executor) {
|
||||
try {
|
||||
// Ignore these - they must have printed an error already
|
||||
if (executor == null)
|
||||
return;
|
||||
|
||||
PluginCommand command = getCommand(name);
|
||||
|
||||
// Try to load the command
|
||||
if (command != null)
|
||||
command.setExecutor(executor);
|
||||
else
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the current plugin.
|
||||
*/
|
||||
private void disablePlugin() {
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
|
||||
private void createPacketTask(Server server) {
|
||||
try {
|
||||
if (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();
|
||||
|
||||
// We KNOW we're on the main thread at the moment
|
||||
manager.sendProcessedPackets(tickCounter++, true);
|
||||
|
||||
// House keeping
|
||||
updateConfiguration();
|
||||
}
|
||||
}, ASYNC_MANAGER_DELAY, ASYNC_MANAGER_DELAY);
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
if (packetTask == -1) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_CREATE_TIMEOUT_TASK).error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateConfiguration() {
|
||||
if (config != null && config.getModificationCount() != configExpectedMod) {
|
||||
configExpectedMod = config.getModificationCount();
|
||||
|
||||
// Update the debug flag
|
||||
protocolManager.setDebug(config.isDebug());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (skipDisable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable compiler
|
||||
if (backgroundCompiler != null) {
|
||||
backgroundCompiler.shutdownAll();
|
||||
backgroundCompiler = null;
|
||||
BackgroundCompiler.setInstance(null);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
if (packetTask >= 0) {
|
||||
getServer().getScheduler().cancelTask(packetTask);
|
||||
packetTask = -1;
|
||||
}
|
||||
|
||||
// And redirect handler too
|
||||
if (redirectHandler != null) {
|
||||
logger.removeHandler(redirectHandler);
|
||||
}
|
||||
if (protocolManager != null)
|
||||
protocolManager.close();
|
||||
else
|
||||
return; // Plugin reloaders!
|
||||
|
||||
if (unhookTask != null)
|
||||
unhookTask.close();
|
||||
protocolManager = null;
|
||||
statistics = null;
|
||||
|
||||
// To clean up global parameters
|
||||
reporter = new BasicErrorReporter();
|
||||
}
|
||||
|
||||
// Get the Bukkit logger first, before we try to create our own
|
||||
private Logger getLoggerSafely() {
|
||||
Logger log = null;
|
||||
|
||||
try {
|
||||
log = getLogger();
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
// Use the default logger instead
|
||||
if (log == null)
|
||||
log = Logger.getLogger("Minecraft");
|
||||
return log;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current error reporter.
|
||||
* <p>
|
||||
* This is guaranteed to not be NULL in 2.5.0 and later.
|
||||
* @return Current error reporter.
|
||||
*/
|
||||
public static ErrorReporter getErrorReporter() {
|
||||
return reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current strongly typed configuration.
|
||||
* @return The configuration, or NULL if ProtocolLib hasn't loaded yet.
|
||||
*/
|
||||
public static ProtocolConfig getConfiguration() {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the packet protocol manager.
|
||||
* @return Packet protocol manager, or NULL if it has been disabled.
|
||||
*/
|
||||
public static ProtocolManager getProtocolManager() {
|
||||
return protocolManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return Metrics instance container.
|
||||
*/
|
||||
public Statistics getStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an executor service for performing asynchronous tasks on the behalf of ProtocolLib.
|
||||
* <p>
|
||||
* Note that this service is NULL if ProtocolLib has not been initialized yet.
|
||||
* @return The executor service, or NULL.
|
||||
*/
|
||||
public static ListeningScheduledExecutorService getExecutorAsync() {
|
||||
return executorAsync;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an executor service for performing synchronous tasks (main thread) on the behalf of ProtocolLib.
|
||||
* <p>
|
||||
* Note that this service is NULL if ProtocolLib has not been initialized yet.
|
||||
* @return The executor service, or NULL.
|
||||
*/
|
||||
public static ListeningScheduledExecutorService getExecutorSync() {
|
||||
return executorSync;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import 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.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.PacketConstructor;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param player - the player.
|
||||
* @return The associated protocol version, or {@link Integer#MIN_VALUE} if unknown.
|
||||
*/
|
||||
public 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.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters)
|
||||
throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
|
||||
throws IllegalAccessException, InvocationTargetException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public 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.
|
||||
* @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.
|
||||
*/
|
||||
public 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 maxObserverDistance - the maximum distance to the origin.
|
||||
*/
|
||||
public void broadcastServerPacket(PacketContainer packet, Location origin, int maxObserverDistance);
|
||||
|
||||
/**
|
||||
* Retrieves a list of every registered packet listener.
|
||||
* @return Every registered packet listener.
|
||||
*/
|
||||
public ImmutableSet<PacketListener> getPacketListeners();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param listener - new packet listener.
|
||||
*/
|
||||
public void addPacketListener(PacketListener listener);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void removePacketListener(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Removes every listener associated with the given plugin.
|
||||
* @param plugin - the plugin to unload.
|
||||
*/
|
||||
public void removePacketListeners(Plugin plugin);
|
||||
|
||||
/**
|
||||
* Constructs a new encapsulated Minecraft packet with the given ID.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #createPacket(PacketType)} instead.
|
||||
* @param id - packet ID.
|
||||
* @return New encapsulated Minecraft packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketContainer createPacket(int id);
|
||||
|
||||
/**
|
||||
* Constructs a new encapsulated Minecraft packet with the given ID.
|
||||
* @param type - packet type.
|
||||
* @return New encapsulated Minecraft packet.
|
||||
*/
|
||||
public 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.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #createPacket(PacketType, boolean)} instead.
|
||||
*
|
||||
* @param id - packet ID.
|
||||
* @param forceDefaults - TRUE to use sensible defaults in most fields, FALSE otherwise.
|
||||
* @return New encapsulated Minecraft packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketContainer createPacket(int id, boolean forceDefaults);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param forceDefaults - TRUE to use sensible defaults in most fields, FALSE otherwise.
|
||||
* @return New encapsulated Minecraft packet.
|
||||
*/
|
||||
public PacketContainer createPacket(PacketType type, boolean forceDefaults);
|
||||
|
||||
/**
|
||||
* Construct a packet using the special builtin Minecraft constructors.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #createPacketConstructor(PacketType, Object...)} instead.
|
||||
* @param id - the packet ID.
|
||||
* @param arguments - arguments that will be passed to the constructor.
|
||||
* @return The packet constructor.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketConstructor createPacketConstructor(int id, Object... arguments);
|
||||
|
||||
/**
|
||||
* Construct a packet using the special builtin Minecraft constructors.
|
||||
* @param id - the packet type.
|
||||
* @param arguments - arguments that will be passed to the constructor.
|
||||
* @return The packet constructor.
|
||||
*/
|
||||
public 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.
|
||||
* @param observers - the clients to update.
|
||||
*/
|
||||
public void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException;
|
||||
|
||||
/**
|
||||
* Retrieve the associated entity.
|
||||
* @param container - the world the entity belongs to.
|
||||
* @param id - the unique ID of the entity.
|
||||
* @return The associated entity.
|
||||
* @throws FieldAccessException Reflection failed.
|
||||
*/
|
||||
public Entity getEntityFromID(World container, int id) throws FieldAccessException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public List<Player> getEntityTrackers(Entity entity) throws FieldAccessException;
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the ID of the sent server packets that will be observed by listeners.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getSendingFilterTypes()} instead.
|
||||
* @return Every filtered server packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Integer> getSendingFilters();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the type of the sent server packets that will be observed by listeners.
|
||||
* @return Every filtered server packet.
|
||||
*/
|
||||
public Set<PacketType> getSendingFilterTypes();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the ID of the received client packets that will be observed by listeners.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getReceivingFilterTypes()} instead.
|
||||
* @return Every filtered client packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Integer> getReceivingFilters();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the type of the received client packets that will be observed by listeners.
|
||||
* @return Every filtered client packet.
|
||||
*/
|
||||
public Set<PacketType> getReceivingFilterTypes();
|
||||
|
||||
/**
|
||||
* Retrieve the current Minecraft version.
|
||||
* @return The current version.
|
||||
*/
|
||||
public MinecraftVersion getMinecraftVersion();
|
||||
|
||||
/**
|
||||
* Determines whether or not this protocol manager has been disabled.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
public boolean isClosed();
|
||||
|
||||
/**
|
||||
* Retrieve the current asynchronous packet manager.
|
||||
* @return Asynchronous packet manager.
|
||||
*/
|
||||
public AsynchronousManager getAsynchronousManager();
|
||||
}
|
||||
+23
-23
@@ -2,39 +2,37 @@
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import 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.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.DiscreteDomains;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.Ranges;
|
||||
|
||||
/**
|
||||
* Used to parse ranges in CommandPacket.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
final class RangeParser {
|
||||
private RangeParser() {}
|
||||
|
||||
class RangeParser {
|
||||
/**
|
||||
* Parse a range from a given text.
|
||||
* @param text - the text.
|
||||
@@ -42,18 +40,20 @@ final class RangeParser {
|
||||
* @return The parsed ranges.
|
||||
*/
|
||||
public static List<Range<Integer>> getRanges(String text, Range<Integer> legalRange) {
|
||||
return getRanges(new ArrayDeque<>(Collections.singletonList(text)), legalRange);
|
||||
return getRanges(new ArrayDeque<String>(Arrays.asList(text)), legalRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse ranges from an array of elements.
|
||||
* @param input the input to parse the ranges from
|
||||
* @param args - array of elements.
|
||||
* @param offset - beginning offset.
|
||||
* @param lastIndex - the last index of the array to read.
|
||||
* @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<>();
|
||||
List<Range<Integer>> ranges = new ArrayList<Range<Integer>>();
|
||||
|
||||
for (int i = 0; i < tokens.size(); i++) {
|
||||
Range<Integer> range;
|
||||
@@ -68,7 +68,7 @@ final class RangeParser {
|
||||
throw new IllegalArgumentException("Cannot form a range without a upper limit.");
|
||||
|
||||
// This is a proper range
|
||||
range = Range.closed(Integer.parseInt(current), Integer.parseInt(tokens.get(i + 2)));
|
||||
range = Ranges.closed(Integer.parseInt(current), Integer.parseInt(tokens.get(i + 2)));
|
||||
ranges.add(range);
|
||||
|
||||
// Skip the two next tokens
|
||||
@@ -76,7 +76,7 @@ final class RangeParser {
|
||||
|
||||
} else {
|
||||
// Just a single number
|
||||
range = Range.singleton(Integer.parseInt(current));
|
||||
range = Ranges.singleton(Integer.parseInt(current));
|
||||
ranges.add(range);
|
||||
}
|
||||
|
||||
@@ -96,13 +96,13 @@ final 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<>();
|
||||
List<Range<Integer>> result = new ArrayList<Range<Integer>>();
|
||||
boolean[] set = new boolean[maximum + 1];
|
||||
int start = -1;
|
||||
|
||||
// Set every ID
|
||||
for (Range<Integer> range : ranges) {
|
||||
for (int id : ContiguousSet.create(range, DiscreteDomain.integers())) {
|
||||
for (int id : range.asSet(DiscreteDomains.integers())) {
|
||||
set[id] = true;
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ final class RangeParser {
|
||||
}
|
||||
} else {
|
||||
if (start >= 0) {
|
||||
result.add(Range.closed(start, i - 1));
|
||||
result.add(Ranges.closed(start, i - 1));
|
||||
start = -1;
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ final class RangeParser {
|
||||
}
|
||||
|
||||
private static List<String> tokenizeInput(Deque<String> input) {
|
||||
List<String> tokens = new ArrayList<>();
|
||||
List<String> tokens = new ArrayList<String>();
|
||||
|
||||
// Tokenize the input
|
||||
while (!input.isEmpty()) {
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.comphenix.protocol.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicate that this API and its descendants are only valid on Spigot.
|
||||
* @author Kristian
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PACKAGE,
|
||||
ElementType.PARAMETER, ElementType.TYPE, ElementType.FIELD})
|
||||
public @interface Spigot {
|
||||
/**
|
||||
* The minimum build number of Spigot where this is valid.
|
||||
* @return The minimum build.
|
||||
*/
|
||||
int minimumBuild();
|
||||
|
||||
/**
|
||||
* The maximum build number of Spigot where this is valid, or Integer.MAX_VALUE if not set.
|
||||
* @return The maximum build number.
|
||||
*/
|
||||
int maximumBuild() default Integer.MAX_VALUE;
|
||||
}
|
||||
+49
-42
@@ -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
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,10 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.PacketStream;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
@@ -31,15 +35,14 @@ import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||
import com.comphenix.protocol.injector.PrioritizedListener;
|
||||
import com.comphenix.protocol.injector.SortedPacketListenerList;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Represents a filter manager for asynchronous packets.
|
||||
@@ -78,7 +81,7 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
/**
|
||||
* Initialize a asynchronous filter manager.
|
||||
* <p>
|
||||
* <b>Internal method</b>. Retrieve the global asynchronous manager from the protocol manager instead.
|
||||
* <b>Internal method</b>. Retrieve the global asynchronous manager from the protocol manager instead.
|
||||
* @param reporter - desired error reporter.
|
||||
* @param scheduler - task scheduler.
|
||||
*/
|
||||
@@ -86,7 +89,7 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
// Initialize timeout listeners
|
||||
this.serverTimeoutListeners = new SortedPacketListenerList();
|
||||
this.clientTimeoutListeners = new SortedPacketListenerList();
|
||||
this.timeoutListeners = ConcurrentHashMap.newKeySet();
|
||||
this.timeoutListeners = Sets.newSetFromMap(new ConcurrentHashMap<PacketListener, Boolean>());
|
||||
|
||||
this.playerSendingHandler = new PlayerSendingHandler(reporter, serverTimeoutListeners, clientTimeoutListeners);
|
||||
this.serverProcessingQueue = new PacketProcessingQueue(playerSendingHandler);
|
||||
@@ -145,7 +148,7 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
ImmutableSet.Builder<PacketListener> builder = ImmutableSet.builder();
|
||||
|
||||
// Add every asynchronous packet listener
|
||||
for (PrioritizedListener<AsyncListenerHandler> handler :
|
||||
for (PrioritizedListener<AsyncListenerHandler> handler :
|
||||
Iterables.concat(serverProcessingQueue.values(), clientProcessingQueue.values())) {
|
||||
builder.add(handler.getListener().getAsyncListener());
|
||||
}
|
||||
@@ -178,11 +181,11 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
|
||||
// Add listener to either or both processing queue
|
||||
if (hasValidWhitelist(sendingWhitelist)) {
|
||||
manager.verifyWhitelist(listener, sendingWhitelist);
|
||||
PacketFilterManager.verifyWhitelist(listener, sendingWhitelist);
|
||||
serverProcessingQueue.addListener(handler, sendingWhitelist);
|
||||
}
|
||||
if (hasValidWhitelist(receivingWhitelist)) {
|
||||
manager.verifyWhitelist(listener, receivingWhitelist);
|
||||
PacketFilterManager.verifyWhitelist(listener, receivingWhitelist);
|
||||
clientProcessingQueue.addListener(handler, receivingWhitelist);
|
||||
}
|
||||
|
||||
@@ -218,7 +221,7 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
if (listener == null)
|
||||
throw new IllegalArgumentException("listener cannot be NULL.");
|
||||
|
||||
AsyncListenerHandler handler =
|
||||
AsyncListenerHandler handler =
|
||||
findHandler(serverProcessingQueue, listener.getSendingWhitelist(), listener);
|
||||
|
||||
if (handler == null) {
|
||||
@@ -229,7 +232,7 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
|
||||
// Search for the first correct handler
|
||||
private AsyncListenerHandler findHandler(PacketProcessingQueue queue, ListeningWhitelist search, PacketListener target) {
|
||||
if (ListeningWhitelist.isEmpty(search))
|
||||
if (ListeningWhitelist.isEmpty(search))
|
||||
return null;
|
||||
|
||||
for (PacketType type : search.getTypes()) {
|
||||
@@ -247,7 +250,7 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
if (handler == null)
|
||||
throw new IllegalArgumentException("listenerToken cannot be NULL");
|
||||
|
||||
handler.cancel();
|
||||
handler.cancel();
|
||||
}
|
||||
|
||||
// Called by AsyncListenerHandler
|
||||
@@ -292,7 +295,7 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
private void unregisterAsyncHandlers(PacketProcessingQueue processingQueue, Plugin plugin) {
|
||||
|
||||
// Iterate through every packet listener
|
||||
for (PrioritizedListener<AsyncListenerHandler> listener : processingQueue.values()) {
|
||||
for (PrioritizedListener<AsyncListenerHandler> listener : processingQueue.values()) {
|
||||
// Remove the listener
|
||||
if (Objects.equal(listener.getListener().getPlugin(), plugin)) {
|
||||
unregisterAsyncHandler(listener.getListener());
|
||||
@@ -302,35 +305,39 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
|
||||
/**
|
||||
* Enqueue a packet for asynchronous processing.
|
||||
*
|
||||
* @param syncPacket - synchronous packet event.
|
||||
* @param asyncMarker - the asynchronous marker to use.
|
||||
*/
|
||||
public synchronized void enqueueSyncPacket(PacketEvent syncPacket, AsyncMarker asyncMarker) {
|
||||
PacketEvent newEvent = PacketEvent.fromSynchronous(syncPacket, asyncMarker);
|
||||
|
||||
|
||||
if (asyncMarker.isQueued() || asyncMarker.isTransmitted())
|
||||
throw new IllegalArgumentException("Cannot queue a packet that has already been queued.");
|
||||
|
||||
|
||||
asyncMarker.setQueuedSendingIndex(asyncMarker.getNewSendingIndex());
|
||||
|
||||
// The player is only be null when they're logged out,
|
||||
// so this should be a pretty safe check
|
||||
Player player = syncPacket.getPlayer();
|
||||
if (player != null) {
|
||||
// Start the process
|
||||
getSendingQueue(syncPacket).enqueue(newEvent);
|
||||
|
||||
// We know this is occurring on the main thread, so pass TRUE
|
||||
getProcessingQueue(syncPacket).enqueue(newEvent, true);
|
||||
}
|
||||
|
||||
// Start the process
|
||||
getSendingQueue(syncPacket).enqueue(newEvent);
|
||||
|
||||
// We know this is occuring on the main thread, so pass TRUE
|
||||
getProcessingQueue(syncPacket).enqueue(newEvent, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<Integer> getSendingFilters() {
|
||||
return PacketRegistry.toLegacy(serverProcessingQueue.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getReceivingTypes() {
|
||||
return serverProcessingQueue.keySet();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<Integer> getReceivingFilters() {
|
||||
return PacketRegistry.toLegacy(clientProcessingQueue.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getSendingTypes() {
|
||||
return clientProcessingQueue.keySet();
|
||||
@@ -355,21 +362,23 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
* @return Asynchronous marker.
|
||||
*/
|
||||
public AsyncMarker createAsyncMarker() {
|
||||
return createAsyncMarker(AsyncMarker.DEFAULT_TIMEOUT_DELTA);
|
||||
return createAsyncMarker(AsyncMarker.DEFAULT_SENDING_DELTA, AsyncMarker.DEFAULT_TIMEOUT_DELTA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an async marker with the given sending priority delta and timeout delta.
|
||||
* @param sendingDelta - how many packets we're willing to wait.
|
||||
* @param timeoutDelta - how long (in ms) until the packet expire.
|
||||
* @return An async marker.
|
||||
*/
|
||||
public AsyncMarker createAsyncMarker(long timeoutDelta) {
|
||||
return createAsyncMarker(timeoutDelta, currentSendingIndex.incrementAndGet());
|
||||
public AsyncMarker createAsyncMarker(long sendingDelta, long timeoutDelta) {
|
||||
return createAsyncMarker(sendingDelta, timeoutDelta,
|
||||
currentSendingIndex.incrementAndGet(), System.currentTimeMillis());
|
||||
}
|
||||
|
||||
// Helper method
|
||||
private AsyncMarker createAsyncMarker(long timeoutDelta, long sendingIndex) {
|
||||
return new AsyncMarker(manager, sendingIndex, System.currentTimeMillis(), timeoutDelta);
|
||||
private AsyncMarker createAsyncMarker(long sendingDelta, long timeoutDelta, long sendingIndex, long currentTime) {
|
||||
return new AsyncMarker(manager, sendingIndex, sendingDelta, System.currentTimeMillis(), timeoutDelta);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -459,8 +468,6 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
|
||||
/**
|
||||
* Send any due packets, or clean up packets that have expired.
|
||||
* @param tickCounter Tick counter
|
||||
* @param onMainThread Whether or not to execute on the main thread
|
||||
*/
|
||||
public void sendProcessedPackets(int tickCounter, boolean onMainThread) {
|
||||
// The server queue is unlikely to need checking that often
|
||||
+79
-61
@@ -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,6 +24,8 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
@@ -34,11 +36,10 @@ import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager.ListenerType;
|
||||
import com.comphenix.protocol.timing.TimedTracker;
|
||||
import com.comphenix.protocol.utility.WrappedScheduler;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents a handler for an asynchronous event.
|
||||
* <p>
|
||||
@@ -47,7 +48,7 @@ import org.bukkit.plugin.Plugin;
|
||||
*/
|
||||
public class AsyncListenerHandler {
|
||||
public static final ReportType REPORT_HANDLER_NOT_STARTED = new ReportType(
|
||||
"Plugin %s did not start the asynchronous handler %s by calling start() or syncStart().");
|
||||
"Plugin %s did not start the asynchronous handler %s by calling start() or syncStart().");
|
||||
|
||||
/**
|
||||
* Signal an end to packet processing.
|
||||
@@ -121,10 +122,16 @@ public class AsyncListenerHandler {
|
||||
}
|
||||
|
||||
private void startWarningTask() {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
private void stopWarningTask() {
|
||||
@@ -202,7 +209,6 @@ public class AsyncListenerHandler {
|
||||
* close a specific worker is less efficient than stopping an arbitrary worker.
|
||||
* <p>
|
||||
* <b>Warning</b>: Never call the run() method in the main thread.
|
||||
* @return The listener loop
|
||||
*/
|
||||
public AsyncRunnable getListenerLoop() {
|
||||
return new AsyncRunnable() {
|
||||
@@ -281,16 +287,19 @@ public class AsyncListenerHandler {
|
||||
final AsyncRunnable listenerLoop = getListenerLoop();
|
||||
|
||||
stopWarningTask();
|
||||
scheduleAsync(() -> {
|
||||
Thread thread = Thread.currentThread();
|
||||
scheduleAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Thread thread = Thread.currentThread();
|
||||
|
||||
String previousName = thread.getName();
|
||||
String workerName = getFriendlyWorkerName(listenerLoop.getID());
|
||||
|
||||
String previousName = thread.getName();
|
||||
String workerName = getFriendlyWorkerName(listenerLoop.getID());
|
||||
|
||||
// Add the friendly worker name
|
||||
thread.setName(workerName);
|
||||
listenerLoop.run();
|
||||
thread.setName(previousName);
|
||||
// Add the friendly worker name
|
||||
thread.setName(workerName);
|
||||
listenerLoop.run();
|
||||
thread.setName(previousName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -306,7 +315,7 @@ public class AsyncListenerHandler {
|
||||
* public Void apply(@Nullable AsyncRunnable workerLoop) {
|
||||
* Thread thread = Thread.currentThread();
|
||||
* int prevPriority = thread.getPriority();
|
||||
*
|
||||
*
|
||||
* thread.setPriority(Thread.MIN_PRIORITY);
|
||||
* workerLoop.run();
|
||||
* thread.setPriority(prevPriority);
|
||||
@@ -326,11 +335,17 @@ public class AsyncListenerHandler {
|
||||
final AsyncRunnable listenerLoop = getListenerLoop();
|
||||
final Function<AsyncRunnable, Void> delegateCopy = executor;
|
||||
|
||||
scheduleAsync(() -> delegateCopy.apply(listenerLoop));
|
||||
scheduleAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delegateCopy.apply(listenerLoop);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void scheduleAsync(Runnable runnable) {
|
||||
listener.getPlugin().getServer().getScheduler().runTaskAsynchronously(listener.getPlugin(), runnable);
|
||||
// Handle deprecation
|
||||
WrappedScheduler.runAsynchronouslyRepeat(listener.getPlugin(), filterManager.getScheduler(), runnable, 0L, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,9 +357,9 @@ public class AsyncListenerHandler {
|
||||
* @return A friendly thread name.
|
||||
*/
|
||||
public String getFriendlyWorkerName(int id) {
|
||||
return String.format("Protocol Worker #%s - %s - [recv: %s, send: %s]",
|
||||
id,
|
||||
PacketAdapter.getPluginName(listener),
|
||||
return String.format("Protocol Worker #%s - %s - [recv: %s, send: %s]",
|
||||
id,
|
||||
PacketAdapter.getPluginName(listener),
|
||||
fromWhitelist(listener.getReceivingWhitelist()),
|
||||
fromWhitelist(listener.getSendingWhitelist())
|
||||
);
|
||||
@@ -368,8 +383,8 @@ public class AsyncListenerHandler {
|
||||
* This is useful if you need to synchronize with the main thread in your packet listener, but
|
||||
* you're not performing any expensive processing.
|
||||
* <p>
|
||||
* <b>Note</b>: Use a asynchronous worker if the packet listener may use more than 0.5 ms
|
||||
* of processing time on a single packet. Do as much as possible on the worker thread, and schedule synchronous tasks
|
||||
* <b>Note</b>: Use a asynchronous worker if the packet listener may use more than 0.5 ms
|
||||
* of processing time on a single packet. Do as much as possible on the worker thread, and schedule synchronous tasks
|
||||
* to use the Bukkit API instead.
|
||||
* @return TRUE if the synchronized processing was successfully started, FALSE if it's already running.
|
||||
* @throws IllegalStateException If we couldn't start the underlying task.
|
||||
@@ -384,12 +399,12 @@ public class AsyncListenerHandler {
|
||||
* This is useful if you need to synchronize with the main thread in your packet listener, but
|
||||
* you're not performing any expensive processing.
|
||||
* <p>
|
||||
* The processing time parameter gives the upper bound for the amount of time spent processing pending packets.
|
||||
* It should be set to a fairly low number, such as 0.5 ms or 1% of a game tick - to reduce the impact
|
||||
* The processing time parameter gives the upper bound for the amount of time spent processing pending packets.
|
||||
* It should be set to a fairly low number, such as 0.5 ms or 1% of a game tick - to reduce the impact
|
||||
* on the main thread. Never go beyond 50 milliseconds.
|
||||
* <p>
|
||||
* <b>Note</b>: Use a asynchronous worker if the packet listener may exceed the ideal processing time
|
||||
* on a single packet. Do as much as possible on the worker thread, and schedule synchronous tasks
|
||||
* <b>Note</b>: Use a asynchronous worker if the packet listener may exceed the ideal processing time
|
||||
* on a single packet. Do as much as possible on the worker thread, and schedule synchronous tasks
|
||||
* to use the Bukkit API instead.
|
||||
*
|
||||
* @param time - the amount of processing time alloted per game tick (20 ticks per second).
|
||||
@@ -409,28 +424,31 @@ public class AsyncListenerHandler {
|
||||
if (syncTask < 0) {
|
||||
stopWarningTask();
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
// Check time here, ensuring that we at least process one packet
|
||||
if (System.nanoTime() < stopTime)
|
||||
break;
|
||||
}
|
||||
}, tickDelay, tickDelay);
|
||||
|
||||
@@ -485,7 +503,7 @@ public class AsyncListenerHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current number of workers.
|
||||
* Set the current number of workers.
|
||||
* <p>
|
||||
* This method can only be called with a count of zero when the listener is closing.
|
||||
* @param count - new number of workers.
|
||||
@@ -545,7 +563,7 @@ public class AsyncListenerHandler {
|
||||
*/
|
||||
private void listenerLoop(int workerID) {
|
||||
// Danger, danger!
|
||||
if (Thread.currentThread().getId() == mainThread.getId())
|
||||
if (Thread.currentThread().getId() == mainThread.getId())
|
||||
throw new IllegalStateException("Do not call this method from the main thread.");
|
||||
if (cancelled)
|
||||
throw new IllegalStateException("Listener has been cancelled. Create a new listener instead.");
|
||||
@@ -566,7 +584,7 @@ public class AsyncListenerHandler {
|
||||
// This is a bit slow, but it should be safe
|
||||
synchronized (stopLock) {
|
||||
// Are we the one who is supposed to stop?
|
||||
if (stoppedTasks.contains(workerID))
|
||||
if (stoppedTasks.contains(workerID))
|
||||
return;
|
||||
if (waitForStops())
|
||||
return;
|
||||
@@ -598,7 +616,7 @@ public class AsyncListenerHandler {
|
||||
AsyncMarker marker = packet.getAsyncMarker();
|
||||
|
||||
// Here's the core of the asynchronous processing
|
||||
try {
|
||||
try {
|
||||
synchronized (marker.getProcessingLock()) {
|
||||
marker.setListenerHandler(this);
|
||||
marker.setWorkerID(workerID);
|
||||
@@ -606,7 +624,7 @@ public class AsyncListenerHandler {
|
||||
// We're not THAT worried about performance here
|
||||
if (timedManager.isTiming()) {
|
||||
// Retrieve the tracker to use
|
||||
TimedTracker tracker = timedManager.getTracker(listener,
|
||||
TimedTracker tracker = timedManager.getTracker(listener,
|
||||
packet.isServerPacket() ? ListenerType.ASYNC_SERVER_SIDE : ListenerType.ASYNC_CLIENT_SIDE);
|
||||
long token = tracker.beginTracking();
|
||||
|
||||
+63
-80
@@ -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,29 +24,25 @@ import java.lang.reflect.Method;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.comphenix.protocol.PacketStream;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.PrioritizedListener;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
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 +52,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;
|
||||
@@ -110,28 +106,28 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
* Create a container for asyncronous packets.
|
||||
* @param initialTime - the current time in milliseconds since 01.01.1970 00:00.
|
||||
*/
|
||||
AsyncMarker(PacketStream packetStream, long sendingIndex, long initialTime, long timeoutDelta) {
|
||||
AsyncMarker(PacketStream packetStream, long sendingIndex, long sendingDelta, 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.
|
||||
* @return The initial time in number of milliseconds since 01.01.1970 00:00.
|
||||
*/
|
||||
public long getInitialTime() {
|
||||
return initialTime;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the time the packet will be forcefully rejected.
|
||||
@@ -140,7 +136,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.
|
||||
@@ -213,19 +209,19 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
* Increment the number of times the current packet must be signalled as done before its transmitted.
|
||||
* <p>
|
||||
* This is useful if an asynchronous listener is waiting for further information before the
|
||||
* packet can be sent to the user. A packet listener <b>MUST</b> eventually call
|
||||
* packet can be sent to the user. A packet listener <b>MUST</b> eventually call
|
||||
* {@link AsyncFilterManager#signalPacketTransmission(PacketEvent)},
|
||||
* even if the packet is cancelled, after this method is called.
|
||||
* even if the packet is cancelled, after this method is called.
|
||||
* <p>
|
||||
* It is recommended that processing outside a packet listener is wrapped in a synchronized block
|
||||
* 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 +229,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 +237,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 +292,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 +301,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 +315,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) {
|
||||
@@ -335,7 +331,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current asynchronous listener handler.
|
||||
* Set the current asynchronous listener handler.
|
||||
* <p>
|
||||
* Used by the worker to update the value.
|
||||
* @param listenerHandler - new listener handler.
|
||||
@@ -369,7 +365,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,29 +373,34 @@ 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 {
|
||||
if (event.isServerPacket()) {
|
||||
packetStream.sendServerPacket(event.getPlayer(), event.getPacket(), NetworkMarker.getNetworkMarker(event), false);
|
||||
} else {
|
||||
packetStream.receiveClientPacket(event.getPlayer(), event.getPacket(), NetworkMarker.getNetworkMarker(event),
|
||||
false);
|
||||
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);
|
||||
}
|
||||
transmitted = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if Minecraft allows asynchronous processing of this packet.
|
||||
* @param event - packet event
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
* @throws FieldAccessException If determining fails for some reasaon
|
||||
*/
|
||||
public boolean isMinecraftAsync(PacketEvent event) throws FieldAccessException {
|
||||
|
||||
if (isMinecraftAsync == null && !alwaysSync) {
|
||||
try {
|
||||
isMinecraftAsync = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethodByName("a_.*");
|
||||
@@ -407,43 +408,25 @@ 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);
|
||||
} else if (methods.size() == 1) {
|
||||
// We're in 1.2.5
|
||||
alwaysSync = true;
|
||||
} else if (MinecraftVersion.BOUNTIFUL_UPDATE.atOrAbove()) {
|
||||
// The centralized async marker was removed in 1.8
|
||||
// Incoming chat packets can be async
|
||||
if (event.getPacketType() == PacketType.Play.Client.CHAT) {
|
||||
String content = event.getPacket().getStrings().readSafely(0);
|
||||
if (content != null) {
|
||||
// Incoming chat packets are async only if they aren't commands
|
||||
return ! content.startsWith("/");
|
||||
} else {
|
||||
ProtocolLogger.log(Level.WARNING, "Failed to determine contents of incoming chat packet!");
|
||||
alwaysSync = true;
|
||||
}
|
||||
} else if (event.getPacketType() == PacketType.Status.Server.SERVER_INFO) {
|
||||
return true;
|
||||
} else {
|
||||
// TODO: Find more cases of async packets
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ProtocolLogger.log(Level.INFO, "Could not determine asynchronous state of packets (this can probably be ignored)");
|
||||
System.err.println("[ProtocolLib] Cannot determine asynchronous state of packets!");
|
||||
alwaysSync = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (alwaysSync) {
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
// Wrap exceptions
|
||||
// Wrap exceptions
|
||||
return (Boolean) isMinecraftAsync.invoke(event.getPacket().getHandle());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new FieldAccessException("Illegal argument", e);
|
||||
@@ -454,7 +437,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(AsyncMarker o) {
|
||||
if (o == null)
|
||||
@@ -462,7 +445,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
else
|
||||
return Longs.compare(getNewSendingIndex(), o.getNewSendingIndex());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
// Standard equals
|
||||
@@ -473,7 +456,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Longs.hashCode(getNewSendingIndex());
|
||||
+6
-7
@@ -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
|
||||
*/
|
||||
|
||||
@@ -35,7 +35,6 @@ public interface AsyncRunnable extends Runnable {
|
||||
* <p>
|
||||
* This may not occur right away.
|
||||
* @return TRUE if the thread was stopped, FALSE if it was already stopped.
|
||||
* @throws InterruptedException if it is interrupted
|
||||
*/
|
||||
public boolean stop() throws InterruptedException;
|
||||
|
||||
+6
-2
@@ -17,10 +17,14 @@
|
||||
|
||||
package com.comphenix.protocol.async;
|
||||
|
||||
import com.comphenix.protocol.events.*;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Represents a NO OPERATION listener.
|
||||
*
|
||||
File renamed without changes.
File renamed without changes.
+83
-72
@@ -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,79 +24,81 @@ import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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<>(INITIAL_CAPACITY);
|
||||
this.sendingQueue = new PriorityBlockingQueue<PacketEventHolder>(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);
|
||||
@@ -105,85 +107,83 @@ 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 occurring on the main thread.
|
||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||
*/
|
||||
public synchronized void signalPacketUpdate(List<PacketType> packetsRemoved, boolean onMainThread) {
|
||||
Set<PacketType> lookup = new HashSet<>(packetsRemoved);
|
||||
|
||||
Set<PacketType> lookup = new HashSet<PacketType>(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,61 +191,65 @@ 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) {
|
||||
// We know this isn't on the main thread
|
||||
asynchronousSender.execute(() -> processPacketHolder(false, holder));
|
||||
|
||||
asynchronousSender.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// We know this isn't on the main thread
|
||||
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 {
|
||||
@@ -253,10 +257,9 @@ 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() {
|
||||
@@ -265,16 +268,24 @@ 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();
|
||||
@@ -288,7 +299,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;
|
||||
}
|
||||
+15
-14
@@ -24,6 +24,8 @@ 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;
|
||||
@@ -31,20 +33,18 @@ 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 final ErrorReporter reporter;
|
||||
private final ConcurrentMap<Player, QueueContainer> playerSendingQueues;
|
||||
private ErrorReporter reporter;
|
||||
private ConcurrentMap<Player, QueueContainer> playerSendingQueues;
|
||||
|
||||
// Timeout listeners
|
||||
private final SortedPacketListenerList serverTimeoutListeners;
|
||||
private final SortedPacketListenerList clientTimeoutListeners;
|
||||
private SortedPacketListenerList serverTimeoutListeners;
|
||||
private SortedPacketListenerList clientTimeoutListeners;
|
||||
|
||||
// Asynchronous packet sending
|
||||
private Executor asynchronousSender;
|
||||
@@ -58,8 +58,8 @@ class PlayerSendingHandler {
|
||||
* @author Kristian
|
||||
*/
|
||||
private class QueueContainer {
|
||||
private final PacketSendingQueue serverQueue;
|
||||
private final PacketSendingQueue clientQueue;
|
||||
private PacketSendingQueue serverQueue;
|
||||
private 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 we're running on the main thread.
|
||||
* @param synchronusOK - whether or not we're running on the main thread.
|
||||
*/
|
||||
public void sendServerPackets(List<PacketType> types, boolean synchronusOK) {
|
||||
if (!cleaningUp) {
|
||||
@@ -186,7 +186,8 @@ class PlayerSendingHandler {
|
||||
|
||||
/**
|
||||
* Immediately send every client packet with the given list of IDs.
|
||||
* @param synchronusOK - whether we're running on the main thread.
|
||||
* @param ids - ID of every packet to send immediately.
|
||||
* @param synchronusOK - whether or not we're running on the main thread.
|
||||
*/
|
||||
public void sendClientPackets(List<PacketType> types, boolean synchronusOK) {
|
||||
if (!cleaningUp) {
|
||||
@@ -198,7 +199,7 @@ class PlayerSendingHandler {
|
||||
|
||||
/**
|
||||
* Send any outstanding server packets.
|
||||
* @param onMainThread - whether this is occurring on the main thread.
|
||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||
*/
|
||||
public void trySendServerPackets(boolean onMainThread) {
|
||||
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||
@@ -208,7 +209,7 @@ class PlayerSendingHandler {
|
||||
|
||||
/**
|
||||
* Send any outstanding server packets.
|
||||
* @param onMainThread - whether this is occurring on the main thread.
|
||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||
*/
|
||||
public void trySendClientPackets(boolean onMainThread) {
|
||||
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||
@@ -221,7 +222,7 @@ class PlayerSendingHandler {
|
||||
* @return Every sever packet queue.
|
||||
*/
|
||||
public List<PacketSendingQueue> getServerQueues() {
|
||||
List<PacketSendingQueue> result = new ArrayList<>();
|
||||
List<PacketSendingQueue> result = new ArrayList<PacketSendingQueue>();
|
||||
|
||||
for (QueueContainer queue : playerSendingQueues.values())
|
||||
result.add(queue.getServerQueue());
|
||||
@@ -233,7 +234,7 @@ class PlayerSendingHandler {
|
||||
* @return Every client packet queue.
|
||||
*/
|
||||
public List<PacketSendingQueue> getClientQueues() {
|
||||
List<PacketSendingQueue> result = new ArrayList<>();
|
||||
List<PacketSendingQueue> result = new ArrayList<PacketSendingQueue>();
|
||||
|
||||
for (QueueContainer queue : playerSendingQueues.values())
|
||||
result.add(queue.getClientQueue());
|
||||
+2
-1
@@ -17,12 +17,13 @@
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
+11
-5
@@ -8,6 +8,7 @@ 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;
|
||||
@@ -48,14 +49,19 @@ public class ExpireHashMap<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<K, ExpireEntry> keyLookup = new HashMap<>();
|
||||
private final PriorityQueue<ExpireEntry> expireQueue = new PriorityQueue<>();
|
||||
private Map<K, ExpireEntry> keyLookup = new HashMap<K, ExpireEntry>();
|
||||
private PriorityQueue<ExpireEntry> expireQueue = new PriorityQueue<ExpireEntry>();
|
||||
|
||||
// View of keyLookup with direct values
|
||||
private final Map<K, V> valueView = Maps.transformValues(keyLookup, entry -> entry.expireValue);
|
||||
private Map<K, V> valueView = Maps.transformValues(keyLookup, new Function<ExpireEntry, V>() {
|
||||
@Override
|
||||
public V apply(ExpireEntry entry) {
|
||||
return entry.expireValue;
|
||||
}
|
||||
});
|
||||
|
||||
// Supplied by the constructor
|
||||
private final Ticker ticker;
|
||||
private Ticker ticker;
|
||||
|
||||
/**
|
||||
* Construct a new hash map where each entry may expire at a given time.
|
||||
@@ -237,4 +243,4 @@ public class ExpireHashMap<K, V> {
|
||||
return valueView.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -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,6 +16,14 @@ public class IntegerMap<T> {
|
||||
private T[] array;
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* Construct a new integer map.
|
||||
* @return A new integer map.
|
||||
*/
|
||||
public static <T> IntegerMap<T> newMap() {
|
||||
return new IntegerMap<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new integer map with a default capacity.
|
||||
*/
|
||||
@@ -43,7 +51,7 @@ public class IntegerMap<T> {
|
||||
public T put(int key, T value) {
|
||||
ensureCapacity(key);
|
||||
|
||||
T old = array[key];
|
||||
T old = array[key];
|
||||
array[key] = Preconditions.checkNotNull(value, "value cannot be NULL");
|
||||
|
||||
if (old == null)
|
||||
@@ -119,7 +127,7 @@ public class IntegerMap<T> {
|
||||
* @return The Integer map.
|
||||
*/
|
||||
public Map<Integer, Object> toMap() {
|
||||
final Map<Integer, Object> map = new HashMap<>();
|
||||
Map<Integer, Object> map = Maps.newHashMap();
|
||||
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i] != null) {
|
||||
+49
-53
@@ -2,25 +2,21 @@
|
||||
* 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;
|
||||
@@ -28,78 +24,81 @@ 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<T> {
|
||||
|
||||
public abstract class AbstractConcurrentListenerMultimap<TListener> {
|
||||
// The core of our map
|
||||
private final ConcurrentMap<PacketType, SortedCopyOnWriteArray<PrioritizedListener<T>>> mapListeners;
|
||||
|
||||
private ConcurrentMap<PacketType, SortedCopyOnWriteArray<PrioritizedListener<TListener>>> mapListeners;
|
||||
|
||||
public AbstractConcurrentListenerMultimap() {
|
||||
this.mapListeners = new ConcurrentHashMap<>();
|
||||
mapListeners = new ConcurrentHashMap<PacketType, SortedCopyOnWriteArray<PrioritizedListener<TListener>>>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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(T listener, ListeningWhitelist whitelist) {
|
||||
PrioritizedListener<T> prioritized = new PrioritizedListener<>(listener, whitelist.getPriority());
|
||||
public void addListener(TListener listener, ListeningWhitelist whitelist) {
|
||||
PrioritizedListener<TListener> prioritized = new PrioritizedListener<TListener>(listener, whitelist.getPriority());
|
||||
|
||||
for (PacketType type : whitelist.getTypes()) {
|
||||
this.addListener(type, prioritized);
|
||||
addListener(type, prioritized);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add the listener to a specific packet notifcation list
|
||||
private void addListener(PacketType type, PrioritizedListener<T> listener) {
|
||||
SortedCopyOnWriteArray<PrioritizedListener<T>> list = this.mapListeners.get(type);
|
||||
|
||||
private void addListener(PacketType type, PrioritizedListener<TListener> listener) {
|
||||
SortedCopyOnWriteArray<PrioritizedListener<TListener>> list = 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<T>> value = new SortedCopyOnWriteArray<PrioritizedListener<T>>();
|
||||
final SortedCopyOnWriteArray<PrioritizedListener<TListener>> value = new SortedCopyOnWriteArray<PrioritizedListener<TListener>>();
|
||||
|
||||
// We may end up creating multiple multisets, but we'll agree on which to use
|
||||
list = this.mapListeners.putIfAbsent(type, value);
|
||||
|
||||
list = 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(T listener, ListeningWhitelist whitelist) {
|
||||
public List<PacketType> removeListener(TListener 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<T>> list = this.mapListeners.get(type);
|
||||
|
||||
SortedCopyOnWriteArray<PrioritizedListener<TListener>> list = 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<T>(listener, whitelist.getPriority()));
|
||||
|
||||
list.remove(new PrioritizedListener<TListener>(listener, whitelist.getPriority()));
|
||||
|
||||
if (list.size() == 0) {
|
||||
this.mapListeners.remove(type);
|
||||
mapListeners.remove(type);
|
||||
removedPackets.add(type);
|
||||
}
|
||||
}
|
||||
@@ -108,41 +107,38 @@ public abstract class AbstractConcurrentListenerMultimap<T> {
|
||||
}
|
||||
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<T>> getListener(PacketType type) {
|
||||
return this.mapListeners.get(type);
|
||||
public Collection<PrioritizedListener<TListener>> getListener(PacketType type) {
|
||||
return mapListeners.get(type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve every listener.
|
||||
*
|
||||
* @return Every listener.
|
||||
*/
|
||||
public Iterable<PrioritizedListener<T>> values() {
|
||||
return Iterables.concat(this.mapListeners.values());
|
||||
public Iterable<PrioritizedListener<TListener>> values() {
|
||||
return Iterables.concat(mapListeners.values());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve every registered packet type:
|
||||
*
|
||||
* @return Registered packet type.
|
||||
*/
|
||||
public Set<PacketType> keySet() {
|
||||
return this.mapListeners.keySet();
|
||||
return mapListeners.keySet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove all packet listeners.
|
||||
*/
|
||||
protected void clearListeners() {
|
||||
this.mapListeners.clear();
|
||||
mapListeners.clear();
|
||||
}
|
||||
}
|
||||
+15
-16
@@ -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
|
||||
*/
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.TreeMap;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.Ranges;
|
||||
|
||||
/**
|
||||
* Represents a generic store of intervals and associated values. No two intervals
|
||||
@@ -67,7 +68,7 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
|
||||
@Override
|
||||
public Range<TKey> getKey() {
|
||||
return Range.closed(left.key, right.key);
|
||||
return Ranges.closed(left.key, right.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,7 +130,7 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
this.state = state;
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To quickly look up ranges we'll index them by endpoints
|
||||
@@ -139,7 +140,6 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
* Removes every interval that intersects with the given range.
|
||||
* @param lowerBound - lowest value to remove.
|
||||
* @param upperBound - highest value to remove.
|
||||
* @return Intervals that were removed
|
||||
*/
|
||||
public Set<Entry> remove(TKey lowerBound, TKey upperBound) {
|
||||
return remove(lowerBound, upperBound, false);
|
||||
@@ -150,13 +150,12 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
* @param lowerBound - lowest value to remove.
|
||||
* @param upperBound - highest value to remove.
|
||||
* @param preserveDifference - whether or not to preserve the intervals that are partially outside.
|
||||
* @return Intervals that were removed
|
||||
*/
|
||||
public Set<Entry> remove(TKey lowerBound, TKey upperBound, boolean preserveDifference) {
|
||||
checkBounds(lowerBound, upperBound);
|
||||
NavigableMap<TKey, EndPoint> range = bounds.subMap(lowerBound, true, upperBound, true);
|
||||
|
||||
EndPoint first = getNextEndPoint(lowerBound, true);
|
||||
EndPoint first = getNextEndPoint(lowerBound, true);
|
||||
EndPoint last = getPreviousEndPoint(upperBound, true);
|
||||
|
||||
// Used while resizing intervals
|
||||
@@ -182,7 +181,7 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
|
||||
if (next != null) {
|
||||
removed.add(getEntry(last, next));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now remove both ranges
|
||||
@@ -254,7 +253,7 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
|
||||
/**
|
||||
* Associates a given interval of keys with a certain value. Any previous
|
||||
* association will be overwritten in the given interval.
|
||||
* association will be overwritten in the given interval.
|
||||
* <p>
|
||||
* Overlapping intervals are not permitted. A key can only be associated with a single value.
|
||||
*
|
||||
@@ -263,7 +262,7 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
* @param value - the value, or NULL to reset this range.
|
||||
*/
|
||||
public void put(TKey lowerBound, TKey upperBound, TValue value) {
|
||||
// While we don't permit overlapping intervals, we'll still allow overwriting existing intervals.
|
||||
// While we don't permit overlapping intervals, we'll still allow overwriting existing intervals.
|
||||
remove(lowerBound, upperBound, true);
|
||||
invokeEntryAdded(putUnsafe(lowerBound, upperBound, value));
|
||||
}
|
||||
@@ -424,7 +423,7 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
if (point != null) {
|
||||
Map.Entry<TKey, EndPoint> previous = bounds.floorEntry(inclusive ? point : decrementKey(point));
|
||||
|
||||
if (previous != null)
|
||||
if (previous != null)
|
||||
return previous.getValue();
|
||||
}
|
||||
return null;
|
||||
@@ -440,7 +439,7 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
if (point != null) {
|
||||
Map.Entry<TKey, EndPoint> next = bounds.ceilingEntry(inclusive ? point : incrementKey(point));
|
||||
|
||||
if (next != null)
|
||||
if (next != null)
|
||||
return next.getValue();
|
||||
}
|
||||
return null;
|
||||
+9
-13
@@ -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
|
||||
*/
|
||||
|
||||
@@ -48,8 +48,6 @@ public class BlockingHashMap<TKey, TValue> {
|
||||
|
||||
/**
|
||||
* Retrieve a cache loader that will always throw an exception.
|
||||
* @param <TKey> Type of the key
|
||||
* @param <TValue> Type of the value
|
||||
* @return An invalid cache loader.
|
||||
*/
|
||||
public static <TKey, TValue> CacheLoader<TKey, TValue> newInvalidCacheLoader() {
|
||||
@@ -85,8 +83,6 @@ public class BlockingHashMap<TKey, TValue> {
|
||||
|
||||
/**
|
||||
* Initialize a new map.
|
||||
* @param <TKey> Type of the key
|
||||
* @param <TValue> Type of the value
|
||||
* @return The created map.
|
||||
*/
|
||||
public static <TKey, TValue> BlockingHashMap<TKey, TValue> create() {
|
||||
@@ -95,7 +91,7 @@ public class BlockingHashMap<TKey, TValue> {
|
||||
|
||||
/**
|
||||
* Waits until a value has been associated with the given key, and then retrieves that value.
|
||||
* @param key - the key whose associated value is to be returned
|
||||
* @param key - the key whose associated value is to be returned
|
||||
* @return The value to which the specified key is mapped.
|
||||
* @throws InterruptedException If the current thread got interrupted while waiting.
|
||||
*/
|
||||
@@ -122,7 +118,7 @@ public class BlockingHashMap<TKey, TValue> {
|
||||
|
||||
/**
|
||||
* Waits until a value has been associated with the given key, and then retrieves that value.
|
||||
* @param key - the key whose associated value is to be returned
|
||||
* @param key - the key whose associated value is to be returned
|
||||
* @param timeout - the amount of time to wait until an association has been made.
|
||||
* @param unit - unit of timeout.
|
||||
* @return The value to which the specified key is mapped, or NULL if the timeout elapsed.
|
||||
@@ -137,7 +133,7 @@ public class BlockingHashMap<TKey, TValue> {
|
||||
* <p>
|
||||
* If timeout is zero, this method will return immediately if it can't find an socket injector.
|
||||
*
|
||||
* @param key - the key whose associated value is to be returned
|
||||
* @param key - the key whose associated value is to be returned
|
||||
* @param timeout - the amount of time to wait until an association has been made.
|
||||
* @param unit - unit of timeout.
|
||||
* @param ignoreInterrupted - TRUE if we should ignore the thread being interrupted, FALSE otherwise.
|
||||
+40
-41
@@ -1,23 +1,24 @@
|
||||
package com.comphenix.protocol.concurrency;
|
||||
|
||||
import com.comphenix.protocol.utility.SafeCacheBuilder;
|
||||
import com.comphenix.protocol.utility.Util;
|
||||
import com.google.common.base.Function;
|
||||
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.util.concurrent.UncheckedExecutionException;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.utility.SafeCacheBuilder;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.RemovalListener;
|
||||
import com.google.common.cache.RemovalNotification;
|
||||
import com.google.common.collect.AbstractIterator;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
|
||||
/**
|
||||
* Represents a concurrent player map.
|
||||
@@ -37,7 +38,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
ADDRESS {
|
||||
@Override
|
||||
public Object apply(Player player) {
|
||||
return player == null ? null : player.getAddress();
|
||||
return player.getAddress();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -47,7 +48,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
NAME {
|
||||
@Override
|
||||
public Object apply(Player player) {
|
||||
return player == null ? null : player.getName();
|
||||
return player.getName();
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -55,41 +56,39 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
/**
|
||||
* An internal map of player keys to values.
|
||||
*/
|
||||
private ConcurrentMap<Object, TValue> valueLookup = createValueMap();
|
||||
protected ConcurrentMap<Object, TValue> valueLookup = createValueMap();
|
||||
|
||||
/**
|
||||
* A cache of the associated keys for each player.
|
||||
*/
|
||||
|
||||
private ConcurrentMap<Object, Player> keyLookup = createKeyCache();
|
||||
|
||||
protected ConcurrentMap<Object, Player> keyLookup = createKeyCache();
|
||||
/**
|
||||
* The method used to retrieve a unique key for a player.
|
||||
*/
|
||||
private final Function<Player, Object> keyMethod;
|
||||
protected final Function<Player, Object> keyMethod;
|
||||
|
||||
/**
|
||||
* Construct a new concurrent player map that uses each player's address as key.
|
||||
* @param <T> Parameter type
|
||||
* @return Concurrent player map.
|
||||
*/
|
||||
public static <T> ConcurrentPlayerMap<T> usingAddress() {
|
||||
return new ConcurrentPlayerMap<>(PlayerKey.ADDRESS);
|
||||
return new ConcurrentPlayerMap<T>(PlayerKey.ADDRESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new concurrent player map that uses each player's name as key.
|
||||
* @param <T> Parameter type
|
||||
* @return Concurrent player map.
|
||||
*/
|
||||
public static <T> ConcurrentPlayerMap<T> usingName() {
|
||||
return new ConcurrentPlayerMap<>(PlayerKey.NAME);
|
||||
return new ConcurrentPlayerMap<T>(PlayerKey.NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new concurrent player map using the given standard key method.
|
||||
* @param standardMethod - the standard key method.
|
||||
*/
|
||||
private ConcurrentPlayerMap(PlayerKey standardMethod) {
|
||||
public ConcurrentPlayerMap(PlayerKey standardMethod) {
|
||||
this.keyMethod = standardMethod;
|
||||
}
|
||||
|
||||
@@ -107,24 +106,27 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
* The default implementation uses a {@link ConcurrentHashMap}.
|
||||
* @return The value map.
|
||||
*/
|
||||
private ConcurrentMap<Object, TValue> createValueMap() {
|
||||
return new ConcurrentHashMap<>();
|
||||
protected ConcurrentMap<Object, TValue> createValueMap() {
|
||||
return Maps.newConcurrentMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a cache of keys and the associated player.
|
||||
* @return The key map.
|
||||
*/
|
||||
private ConcurrentMap<Object, Player> createKeyCache() {
|
||||
protected ConcurrentMap<Object, Player> createKeyCache() {
|
||||
return SafeCacheBuilder.newBuilder().
|
||||
weakValues().
|
||||
removalListener(
|
||||
(RemovalListener<Object, Player>) removed -> {
|
||||
// We ignore explicit removal
|
||||
if (removed.wasEvicted()) {
|
||||
onCacheEvicted(removed.getKey());
|
||||
}
|
||||
}).
|
||||
new RemovalListener<Object, Player>() {
|
||||
@Override
|
||||
public void onRemoval(RemovalNotification<Object, Player> removed) {
|
||||
// We ignore explicit removal
|
||||
if (removed.wasEvicted()) {
|
||||
onCacheEvicted(removed.getKey());
|
||||
}
|
||||
}
|
||||
}).
|
||||
build(
|
||||
new CacheLoader<Object, Player>() {
|
||||
@Override
|
||||
@@ -144,6 +146,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
/**
|
||||
* Invoked when an entry in the cache has been evicted, typically by the garbage collector.
|
||||
* @param key - the key.
|
||||
* @param player - the value that was evicted or collected.
|
||||
*/
|
||||
private void onCacheEvicted(Object key) {
|
||||
Player newPlayer = findOnlinePlayer(key);
|
||||
@@ -161,7 +164,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
* @param key - a non-null key.
|
||||
* @return The player with the given key, or NULL if not found.
|
||||
*/
|
||||
private Player findOnlinePlayer(Object key) {
|
||||
protected Player findOnlinePlayer(Object key) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (key.equals(keyMethod.apply(player))) {
|
||||
return player;
|
||||
@@ -174,8 +177,9 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
* Lookup a player by key in the cache, optionally searching every online player.
|
||||
* @param key - the key of the player we are locating.
|
||||
* @return The player, or NULL if not found.
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
private Player lookupPlayer(Object key) {
|
||||
protected Player lookupPlayer(Object key) {
|
||||
try {
|
||||
return keyLookup.get(key);
|
||||
} catch (UncheckedExecutionException e) {
|
||||
@@ -188,9 +192,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
* @param player - the player whose key we want to retrieve.
|
||||
* @return The key.
|
||||
*/
|
||||
private Object cachePlayerKey(Player player) {
|
||||
Preconditions.checkNotNull(player, "player cannot be null");
|
||||
|
||||
protected Object cachePlayerKey(Player player) {
|
||||
Object key = keyMethod.apply(player);
|
||||
|
||||
keyLookup.put(key, player);
|
||||
@@ -291,7 +293,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
private Iterator<Entry<Player, TValue>> entryIterator() {
|
||||
// Skip entries with stale data
|
||||
final Iterator<Entry<Object, TValue>> source = valueLookup.entrySet().iterator();
|
||||
final AbstractIterator<Entry<Player,TValue>> filtered =
|
||||
final AbstractIterator<Entry<Player,TValue>> filtered =
|
||||
new AbstractIterator<Entry<Player,TValue>>() {
|
||||
@Override
|
||||
protected Entry<Player, TValue> computeNext() {
|
||||
@@ -313,15 +315,12 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
|
||||
|
||||
// We can't return AbstractIterator directly, as it doesn't permitt the remove() method
|
||||
return new Iterator<Entry<Player, TValue>>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return filtered.hasNext();
|
||||
}
|
||||
@Override
|
||||
public Entry<Player, TValue> next() {
|
||||
return filtered.next();
|
||||
}
|
||||
@Override
|
||||
public void remove() {
|
||||
source.remove();
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 OutOfBoundsException 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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());
|
||||
|
||||
public PacketTypeSet() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public PacketTypeSet(Collection<? extends PacketType> values) {
|
||||
for (PacketType type : values) {
|
||||
addType(type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a particular type to the set.
|
||||
* @param type - the type to add.
|
||||
*/
|
||||
public synchronized void addType(PacketType type) {
|
||||
Class<?> packetClass = getPacketClass(type);
|
||||
types.add(Preconditions.checkNotNull(type, "type cannot be NULL."));
|
||||
|
||||
if (packetClass != null) {
|
||||
classes.add(getPacketClass(type));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given types to the set of packet types.
|
||||
* @param types - the types to add.
|
||||
*/
|
||||
public synchronized void addAll(Iterable<? extends PacketType> types) {
|
||||
for (PacketType type : types) {
|
||||
addType(type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a particular type to the set.
|
||||
* @param type - the type to remove.
|
||||
*/
|
||||
public synchronized void removeType(PacketType type) {
|
||||
Class<?> packetClass = getPacketClass(type);
|
||||
types.remove(Preconditions.checkNotNull(type, "type cannot be NULL."));
|
||||
|
||||
if (packetClass != null) {
|
||||
classes.remove(getPacketClass(type));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given types from the set.
|
||||
* @param type - the types to remove.
|
||||
*/
|
||||
public synchronized void removeAll(Iterable<? extends PacketType> types) {
|
||||
for (PacketType type : types) {
|
||||
removeType(type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the packet class associated with a particular type.
|
||||
* @param type - the packet type.
|
||||
* @return The associated packet type.
|
||||
*/
|
||||
protected Class<?> getPacketClass(PacketType type) {
|
||||
return PacketRegistry.getPacketClassFromType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given packet type exists in the set.
|
||||
* @param type - the type to find.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public boolean contains(PacketType type) {
|
||||
return 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a view of this packet type set.
|
||||
* @return The packet type values.
|
||||
*/
|
||||
public Set<PacketType> values() {
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of entries in the set.
|
||||
* @return The number of entries.
|
||||
*/
|
||||
public int size() {
|
||||
return types.size();
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
types.clear();
|
||||
classes.clear();
|
||||
}
|
||||
}
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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.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>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>();
|
||||
|
||||
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)
|
||||
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 = 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)
|
||||
return false;
|
||||
|
||||
List<T> copy = new ArrayList<T>();
|
||||
|
||||
copy.addAll(list);
|
||||
copy.removeAll(values);
|
||||
|
||||
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);
|
||||
copy.removeAll(values);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
list = new ArrayList<T>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object value) {
|
||||
return list.contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> values) {
|
||||
return list.containsAll(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return list.toArray();
|
||||
}
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return list.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return list.toString();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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>
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
package com.comphenix.protocol.error;
|
||||
|
||||
import com.comphenix.protocol.error.Report.ReportBuilder;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.error.Report.ReportBuilder;
|
||||
|
||||
/**
|
||||
* Construct an error reporter that delegates to another error reporter.
|
||||
* @author Kristian
|
||||
+32
-49
@@ -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
|
||||
*/
|
||||
|
||||
@@ -30,8 +30,11 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
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.collections.ExpireHashMap;
|
||||
import com.comphenix.protocol.error.Report.ReportBuilder;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
@@ -39,11 +42,6 @@ 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.
|
||||
*
|
||||
@@ -51,13 +49,13 @@ import org.bukkit.plugin.Plugin;
|
||||
*/
|
||||
public class DetailedErrorReporter implements ErrorReporter {
|
||||
/**
|
||||
* Report format for printing the current exception count.
|
||||
* Report format for printing the current exception count.
|
||||
*/
|
||||
public static final ReportType REPORT_EXCEPTION_COUNT = new ReportType("Internal exception count: %s!");
|
||||
|
||||
public static final String SECOND_LEVEL_PREFIX = " ";
|
||||
public static final String DEFAULT_PREFIX = " ";
|
||||
public static final String DEFAULT_SUPPORT_URL = "https://github.com/dmulloy2/ProtocolLib/issues";
|
||||
public static final String DEFAULT_SUPPORT_URL = "http://dev.bukkit.org/server-mods/protocollib/";
|
||||
|
||||
// Users that are informed about errors in the chat
|
||||
public static final String ERROR_PERMISSION = "protocol.info";
|
||||
@@ -80,7 +78,7 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
protected String pluginName;
|
||||
|
||||
// Whether or not Apache Commons is not present
|
||||
protected static boolean apacheCommonsMissing;
|
||||
protected boolean apacheCommonsMissing;
|
||||
|
||||
// Whether or not detailed errror reporting is enabled
|
||||
protected boolean detailedReporting;
|
||||
@@ -90,11 +88,10 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
|
||||
// Reports to ignore
|
||||
private ExpireHashMap<Report, Boolean> rateLimited = new ExpireHashMap<Report, Boolean>();
|
||||
private final Object rateLock = new Object();
|
||||
private Object rateLock = new Object();
|
||||
|
||||
/**
|
||||
* Create a default error reporting system.
|
||||
* @param plugin - the plugin owner.
|
||||
*/
|
||||
public DetailedErrorReporter(Plugin plugin) {
|
||||
this(plugin, DEFAULT_PREFIX, DEFAULT_SUPPORT_URL);
|
||||
@@ -123,21 +120,13 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
throw new IllegalArgumentException("Plugin cannot be NULL.");
|
||||
|
||||
this.pluginReference = new WeakReference<Plugin>(plugin);
|
||||
this.pluginName = getNameSafely(plugin);
|
||||
this.pluginName = plugin.getName();
|
||||
this.prefix = prefix;
|
||||
this.supportURL = supportURL;
|
||||
this.maxErrorCount = maxErrorCount;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private String getNameSafely(Plugin plugin) {
|
||||
try {
|
||||
return plugin.getName();
|
||||
} catch (LinkageError e) {
|
||||
return "ProtocolLib";
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to get the logger.
|
||||
private static Logger getBukkitLogger() {
|
||||
try {
|
||||
@@ -203,14 +192,14 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
|
||||
// See if we should print the full error
|
||||
if (errorCount < getMaxErrorCount()) {
|
||||
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception occurred in " +
|
||||
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception occured in " +
|
||||
methodName + " for " + pluginName, error);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// Nope - only print the error count occasionally
|
||||
// Nope - only print the error count occationally
|
||||
if (isPowerOfTwo(errorCount)) {
|
||||
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception number " + errorCount + " occurred in " +
|
||||
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception number " + errorCount + " occured in " +
|
||||
methodName + " for " + pluginName, error);
|
||||
}
|
||||
return false;
|
||||
@@ -283,7 +272,7 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
// Print the main warning
|
||||
if (report.getException() != null) {
|
||||
logger.log(level, message, report.getException());
|
||||
} else {
|
||||
} else {
|
||||
logger.log(level, message);
|
||||
|
||||
// Remember the call stack
|
||||
@@ -347,7 +336,7 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
writer.println("at " + supportURL + " with the following data:");
|
||||
|
||||
// Now, let us print important exception information
|
||||
writer.println("Stack Trace:");
|
||||
writer.println(" ===== STACK TRACE =====");
|
||||
|
||||
if (report.getException() != null) {
|
||||
report.getException().printStackTrace(writer);
|
||||
@@ -357,7 +346,7 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
}
|
||||
|
||||
// Data dump!
|
||||
writer.println("Dump:");
|
||||
writer.println(" ===== DUMP =====");
|
||||
|
||||
// Relevant parameters
|
||||
if (report.hasCallerParameters()) {
|
||||
@@ -367,7 +356,7 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
// Global parameters
|
||||
for (String param : globalParameters()) {
|
||||
writer.println(SECOND_LEVEL_PREFIX + param + ":");
|
||||
writer.println(addPrefix(getStringDescription(getGlobalParameter(param)),
|
||||
writer.println(addPrefix(getStringDescription(getGlobalParameter(param)),
|
||||
SECOND_LEVEL_PREFIX + SECOND_LEVEL_PREFIX));
|
||||
}
|
||||
|
||||
@@ -380,20 +369,16 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
writer.println("Version:");
|
||||
writer.println(addPrefix(plugin.toString(), SECOND_LEVEL_PREFIX));
|
||||
}
|
||||
|
||||
// And java version
|
||||
writer.println("Java Version:");
|
||||
writer.println(addPrefix(System.getProperty("java.version"), SECOND_LEVEL_PREFIX));
|
||||
|
||||
|
||||
// Add the server version too
|
||||
if (Bukkit.getServer() != null) {
|
||||
writer.println("Server:");
|
||||
writer.println(addPrefix(Bukkit.getServer().getVersion(), SECOND_LEVEL_PREFIX));
|
||||
|
||||
|
||||
// Inform of this occurrence
|
||||
if (ERROR_PERMISSION != null && ProtocolLibrary.getConfig().isChatWarnings()) {
|
||||
if (ERROR_PERMISSION != null) {
|
||||
Bukkit.getServer().broadcast(
|
||||
String.format("Error %s (%s) occurred in %s.", report.getReportMessage(), report.getException(), sender),
|
||||
String.format("Error %s (%s) occured in %s.", report.getReportMessage(), report.getException(), sender),
|
||||
ERROR_PERMISSION
|
||||
);
|
||||
}
|
||||
@@ -456,7 +441,7 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
* @param value - object to convert.
|
||||
* @return String representation.
|
||||
*/
|
||||
public static String getStringDescription(Object value) {
|
||||
protected String getStringDescription(Object value) {
|
||||
// We can't only rely on toString.
|
||||
if (value == null) {
|
||||
return "[NULL]";
|
||||
@@ -465,15 +450,13 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
} else {
|
||||
try {
|
||||
if (!apacheCommonsMissing)
|
||||
return ToStringBuilder.reflectionToString(value, ToStringStyle.MULTI_LINE_STYLE, false, null);
|
||||
return (ToStringBuilder.reflectionToString(value, ToStringStyle.MULTI_LINE_STYLE, false, null));
|
||||
} catch (LinkageError ex) {
|
||||
// Apache is probably missing
|
||||
apacheCommonsMissing = true;
|
||||
} catch (ThreadDeath | OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (Throwable ex) {
|
||||
} catch (Exception e) {
|
||||
// Don't use the error logger to log errors in error logging (that could lead to infinite loops)
|
||||
ProtocolLogger.log(Level.WARNING, "Cannot convert to a String with Apache: " + ex.getMessage());
|
||||
System.err.print("[ProtocolLib] Warning: Cannot convert to a String with Apache: " + e.getMessage());
|
||||
}
|
||||
|
||||
// Use our custom object printer instead
|
||||
@@ -490,7 +473,7 @@ public class DetailedErrorReporter implements ErrorReporter {
|
||||
* @param test - the object to test.
|
||||
* @return TRUE if this object is simple enough to simply be printed, FALSE othewise.
|
||||
*/
|
||||
protected static boolean isSimpleType(Object test) {
|
||||
protected boolean isSimpleType(Object test) {
|
||||
return test instanceof String || Primitives.isWrapperType(test.getClass());
|
||||
}
|
||||
|
||||
+12
-12
@@ -2,25 +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.error;
|
||||
|
||||
import com.comphenix.protocol.error.Report.ReportBuilder;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.error.Report.ReportBuilder;
|
||||
|
||||
/**
|
||||
* Represents an object that can forward an error {@link Report} to the display and permanent storage.
|
||||
*
|
||||
@@ -56,20 +56,20 @@ public interface ErrorReporter {
|
||||
/**
|
||||
* Prints a debug message from the current sender.
|
||||
* @param sender - the sender.
|
||||
* @param builder - the report builder.
|
||||
* @param report - the report builder.
|
||||
*/
|
||||
public abstract void reportDebug(Object sender, ReportBuilder builder);
|
||||
|
||||
/**
|
||||
* Prints a warning message from the current plugin.
|
||||
* @param sender - the object containing the caller method.
|
||||
* @param sender - the object containing the caller method.
|
||||
* @param report - an error report to include.
|
||||
*/
|
||||
public abstract void reportWarning(Object sender, Report report);
|
||||
|
||||
/**
|
||||
* Prints a warning message from the current plugin.
|
||||
* @param sender - the object containing the caller method.
|
||||
* @param sender - the object containing the caller method.
|
||||
* @param reportBuilder - an error report builder that will be used to get the report.
|
||||
*/
|
||||
public abstract void reportWarning(Object sender, ReportBuilder reportBuilder);
|
||||
@@ -87,4 +87,4 @@ public interface ErrorReporter {
|
||||
* @param reportBuilder - an error report builder that will be used to get the report.
|
||||
*/
|
||||
public abstract void reportDetailed(Object sender, ReportBuilder reportBuilder);
|
||||
}
|
||||
}
|
||||
+15
-14
@@ -1,14 +1,15 @@
|
||||
package com.comphenix.protocol.error;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
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;
|
||||
@@ -28,11 +29,11 @@ public final class PluginContext {
|
||||
|
||||
for (int i = 1; i < elements.length; i++) {
|
||||
String caller = getPluginName(elements[i]);
|
||||
|
||||
if (caller != null && !caller.equals(current)) {
|
||||
return caller;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -40,28 +41,30 @@ public final class PluginContext {
|
||||
* Lookup the plugin that this method invocation belongs to, and return its file name.
|
||||
* @param element - the method invocation.
|
||||
* @return Plugin name, or NULL if not found.
|
||||
*
|
||||
*/
|
||||
public static String getPluginName(StackTraceElement element) {
|
||||
try {
|
||||
if (Bukkit.getServer() == null) {
|
||||
if (Bukkit.getServer() == null)
|
||||
return null;
|
||||
}
|
||||
|
||||
CodeSource codeSource = Class.forName(element.getClassName()).getProtectionDomain().getCodeSource();
|
||||
|
||||
if (codeSource != null) {
|
||||
String encoding = codeSource.getLocation().getPath();
|
||||
File path = new File(URLDecoder.decode(encoding, "UTF-8"));
|
||||
File plugins = getPluginFolder();
|
||||
|
||||
if (plugins != null && folderContains(plugins, path)) {
|
||||
return path.getName().replaceAll(".jar", "");
|
||||
return path.getName();
|
||||
}
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
// throw new RuntimeException("Cannot lookup plugin name.", ex);
|
||||
return null; // Cannot find it
|
||||
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("Cannot lookup plugin name.", e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Cannot lookup plugin name.", e);
|
||||
}
|
||||
|
||||
return null; // Cannot find it
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +86,6 @@ public final class PluginContext {
|
||||
return true;
|
||||
file = file.getParentFile();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -94,7 +96,7 @@ public final class PluginContext {
|
||||
private static File getPluginFolder() {
|
||||
File folder = pluginFolder;
|
||||
|
||||
if (folder == null && Bukkit.getServer() != null) {
|
||||
if (folder == null && Bukkit.getServer() != null) {
|
||||
Plugin[] plugins = Bukkit.getPluginManager().getPlugins();
|
||||
|
||||
if (plugins.length > 0) {
|
||||
@@ -102,7 +104,6 @@ public final class PluginContext {
|
||||
pluginFolder = folder;
|
||||
}
|
||||
}
|
||||
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
+11
-10
@@ -1,15 +1,16 @@
|
||||
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.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class Report {
|
||||
public class Report {
|
||||
private final ReportType type;
|
||||
private final Throwable exception;
|
||||
private final Object[] messageParameters;
|
||||
@@ -75,7 +76,7 @@ public class Report {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum number of nanoseconds to wait until a report of equal type and parameters
|
||||
* Set the minimum number of nanoseconds to wait until a report of equal type and parameters
|
||||
* is allowed to be printed again.
|
||||
* @param rateLimit - number of nanoseconds, or 0 to disable. Cannot be negative.
|
||||
* @return This builder, for chaining.
|
||||
@@ -121,8 +122,8 @@ public class Report {
|
||||
* @param type - the report type that will be used to construct the message.
|
||||
* @param messageParameters - parameters used to construct the report message.
|
||||
* @param callerParameters - parameters from the caller method.
|
||||
*/
|
||||
protected Report(ReportType type, @Nullable Throwable exception,
|
||||
*/
|
||||
protected Report(ReportType type, @Nullable Throwable exception,
|
||||
@Nullable Object[] messageParameters, @Nullable Object[] callerParameters) {
|
||||
this(type, exception, messageParameters, callerParameters, 0);
|
||||
}
|
||||
@@ -134,8 +135,8 @@ public class Report {
|
||||
* @param messageParameters - parameters used to construct the report message.
|
||||
* @param callerParameters - parameters from the caller method.
|
||||
* @param rateLimit - minimum number of nanoseconds to wait until a report of equal type and parameters is allowed to be printed again.
|
||||
*/
|
||||
protected Report(ReportType type, @Nullable Throwable exception,
|
||||
*/
|
||||
protected Report(ReportType type, @Nullable Throwable exception,
|
||||
@Nullable Object[] messageParameters, @Nullable Object[] callerParameters, long rateLimit) {
|
||||
if (type == null)
|
||||
throw new IllegalArgumentException("type cannot be NULL.");
|
||||
@@ -156,7 +157,7 @@ public class Report {
|
||||
|
||||
/**
|
||||
* Retrieve the message parameters that will be used to construc the report message.
|
||||
* <p>
|
||||
* <p<
|
||||
* This should not be confused with the method parameters of the caller method.
|
||||
* @return Message parameters.
|
||||
*/
|
||||
@@ -230,8 +231,8 @@ public class Report {
|
||||
return true;
|
||||
if (obj instanceof Report) {
|
||||
Report other = (Report) obj;
|
||||
return type == other.type &&
|
||||
Arrays.equals(callerParameters, other.callerParameters) &&
|
||||
return type == other.type &&
|
||||
Arrays.equals(callerParameters, other.callerParameters) &&
|
||||
Arrays.equals(messageParameters, other.messageParameters);
|
||||
}
|
||||
return false;
|
||||
+10
-9
@@ -55,7 +55,7 @@ public class ReportType {
|
||||
*/
|
||||
public static Class<?> getSenderClass(Object sender) {
|
||||
if (sender == null)
|
||||
throw new IllegalArgumentException("sender cannot be null.");
|
||||
throw new IllegalArgumentException("sender cannot be NUll.");
|
||||
else if (sender instanceof Class<?>)
|
||||
return (Class<?>) sender;
|
||||
else
|
||||
@@ -75,7 +75,7 @@ public class ReportType {
|
||||
*/
|
||||
public static String getReportName(Object sender, ReportType type) {
|
||||
if (sender == null)
|
||||
throw new IllegalArgumentException("sender cannot be null.");
|
||||
throw new IllegalArgumentException("sender cannot be NUll.");
|
||||
return getReportName(getSenderClass(sender), type);
|
||||
}
|
||||
|
||||
@@ -83,13 +83,13 @@ public class ReportType {
|
||||
* Retrieve the full canonical name of a given report type.
|
||||
* <p>
|
||||
* This is in the format <i>canonical_name_of_class#report_type</i>
|
||||
* @param sender - the sender class.
|
||||
* @param clazz - the sender class.
|
||||
* @param type - the report instance.
|
||||
* @return The full canonical name.
|
||||
*/
|
||||
private static String getReportName(Class<?> sender, ReportType type) {
|
||||
if (sender == null)
|
||||
throw new IllegalArgumentException("sender cannot be null.");
|
||||
throw new IllegalArgumentException("sender cannot be NUll.");
|
||||
|
||||
// Whether or not we need to retrieve the report name again
|
||||
if (type.reportName == null) {
|
||||
@@ -138,10 +138,11 @@ public class ReportType {
|
||||
* @return All associated report fields.
|
||||
*/
|
||||
private static List<Field> getReportFields(Class<?> clazz) {
|
||||
return FuzzyReflection.fromClass(clazz, true)
|
||||
.getFieldList(FuzzyFieldContract.newBuilder()
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.typeDerivedOf(ReportType.class)
|
||||
.build());
|
||||
return FuzzyReflection.fromClass(clazz).getFieldList(
|
||||
FuzzyFieldContract.newBuilder().
|
||||
requireModifier(Modifier.STATIC).
|
||||
typeDerivedOf(ReportType.class).
|
||||
build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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
|
||||
File renamed without changes.
+11
-13
@@ -4,30 +4,28 @@ import com.comphenix.protocol.injector.GamePhase;
|
||||
|
||||
/**
|
||||
* Represents additional options a listener may require.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public enum ListenerOptions {
|
||||
|
||||
/**
|
||||
* Disable the automatic game phase detection that will normally force {@link GamePhase#LOGIN} when a packet ID is
|
||||
* known to be transmitted during login.
|
||||
* 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_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,
|
||||
|
||||
/**
|
||||
* Notify ProtocolLib that {@link PacketListener#onPacketReceiving(PacketEvent)} must be executed on the main server
|
||||
* thread.
|
||||
*/
|
||||
SYNC
|
||||
ASYNC;
|
||||
}
|
||||
+7
-7
@@ -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
|
||||
*/
|
||||
|
||||
@@ -47,7 +47,7 @@ public enum ListenerPriority {
|
||||
HIGHEST(4),
|
||||
/**
|
||||
* Event is listened to purely for monitoring the outcome of an event.
|
||||
* <p>
|
||||
* <p/>
|
||||
* No modifications to the event should be made under this priority.
|
||||
*/
|
||||
MONITOR(5);
|
||||
+212
-124
@@ -2,35 +2,39 @@
|
||||
* 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.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
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
|
||||
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ListeningWhitelist {
|
||||
|
||||
/**
|
||||
* A whitelist with no packets - indicates that the listener shouldn't observe any packets.
|
||||
*/
|
||||
@@ -40,87 +44,95 @@ public class ListeningWhitelist {
|
||||
private final GamePhase gamePhase;
|
||||
private final Set<ListenerOptions> options;
|
||||
private final Set<PacketType> types;
|
||||
|
||||
|
||||
// Cache whitelist
|
||||
private transient Set<Integer> intWhitelist;
|
||||
|
||||
private ListeningWhitelist(Builder builder) {
|
||||
this.priority = builder.priority;
|
||||
this.types = builder.types;
|
||||
this.gamePhase = builder.gamePhase;
|
||||
this.options = builder.options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #newBuilder()} instead.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - set of IDs to observe/enable.
|
||||
*/
|
||||
@Deprecated
|
||||
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist) {
|
||||
this(priority, whitelist, GamePhase.PLAYING);
|
||||
}
|
||||
|
||||
private ListeningWhitelist(ListenerPriority priority) {
|
||||
/**
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #newBuilder()} instead.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - set of IDs to observe/enable.
|
||||
* @param gamePhase - which game phase to receieve notifications on.
|
||||
*/
|
||||
@Deprecated
|
||||
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist, GamePhase gamePhase) {
|
||||
this.priority = priority;
|
||||
this.types = new HashSet<>();
|
||||
this.types = PacketRegistry.toPacketTypes(safeSet(whitelist));
|
||||
this.gamePhase = gamePhase;
|
||||
this.options = EnumSet.noneOf(ListenerOptions.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist of a given priority for a list of packets.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #newBuilder()} instead.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - list of packet IDs to observe/enable.
|
||||
*/
|
||||
@Deprecated
|
||||
public ListeningWhitelist(ListenerPriority priority, Integer... whitelist) {
|
||||
this.priority = priority;
|
||||
this.types = PacketRegistry.toPacketTypes(Sets.newHashSet(whitelist));
|
||||
this.gamePhase = GamePhase.PLAYING;
|
||||
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.
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #newBuilder()} instead.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - list of packet IDs to observe/enable.
|
||||
* @param gamePhase - which game phase to receieve notifications on.
|
||||
*/
|
||||
public static boolean isEmpty(ListeningWhitelist whitelist) {
|
||||
if (whitelist == EMPTY_WHITELIST) {
|
||||
return true;
|
||||
} else if (whitelist == null) {
|
||||
return true;
|
||||
} else {
|
||||
return whitelist.getTypes().isEmpty();
|
||||
}
|
||||
@Deprecated
|
||||
public ListeningWhitelist(ListenerPriority priority, Integer[] whitelist, GamePhase gamePhase) {
|
||||
this.priority = priority;
|
||||
this.types = PacketRegistry.toPacketTypes(Sets.newHashSet(whitelist));
|
||||
this.gamePhase = gamePhase;
|
||||
this.options = EnumSet.noneOf(ListenerOptions.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new builder of whitelists.
|
||||
*
|
||||
* @return New whitelist builder.
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs and options.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #newBuilder()} instead.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - list of packet IDs to observe/enable.
|
||||
* @param gamePhase - which game phase to receieve notifications on.
|
||||
* @param options - every special option associated with this whitelist.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
@Deprecated
|
||||
public ListeningWhitelist(ListenerPriority priority, Integer[] whitelist, GamePhase gamePhase, ListenerOptions... options) {
|
||||
this.priority = priority;
|
||||
this.types = PacketRegistry.toPacketTypes(Sets.newHashSet(whitelist));
|
||||
this.gamePhase = gamePhase;
|
||||
this.options = safeEnumSet(Arrays.asList(options), ListenerOptions.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this whitelist has any enabled packets.
|
||||
*
|
||||
* @return TRUE if there are any packets, FALSE otherwise.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
@@ -129,34 +141,43 @@ 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() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of packets that will be observed by the listeners.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getTypes()} instead.
|
||||
* @return Packet whitelist.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Integer> getWhitelist() {
|
||||
if (intWhitelist == null)
|
||||
intWhitelist = PacketRegistry.toLegacy(types);
|
||||
return intWhitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
@@ -168,6 +189,37 @@ public class ListeningWhitelist {
|
||||
return Objects.hashCode(priority, types, gamePhase, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if any of the given IDs can be found in the whitelist.
|
||||
* @param whitelist - whitelist to test.
|
||||
* @param idList - list of packet IDs to find.
|
||||
* @return TRUE if any of the packets in the list can be found in the whitelist, FALSE otherwise.
|
||||
*/
|
||||
public static boolean containsAny(ListeningWhitelist whitelist, int... idList) {
|
||||
if (whitelist != null) {
|
||||
for (int i = 0; i < idList.length; i++) {
|
||||
if (whitelist.getWhitelist().contains(idList[i]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
@@ -183,30 +235,72 @@ public class ListeningWhitelist {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this == EMPTY_WHITELIST) {
|
||||
if (this == EMPTY_WHITELIST)
|
||||
return "EMPTY_WHITELIST";
|
||||
else
|
||||
return Objects.toStringHelper(this).
|
||||
add("priority", priority).
|
||||
add("packets", types).
|
||||
add("gamephase", gamePhase).
|
||||
add("options", options).
|
||||
toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 "ListeningWhitelist[priority=" + priority + ", packets=" + types + ", gamephase=" + gamePhase
|
||||
+ ", options=" + options + "]";
|
||||
return EnumSet.noneOf(enumClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a copy of a given set.
|
||||
* @param list - 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 = new HashSet<>();
|
||||
private Set<PacketType> types = Sets.newHashSet();
|
||||
private GamePhase gamePhase = GamePhase.PLAYING;
|
||||
private Set<ListenerOptions> options = new HashSet<>();
|
||||
|
||||
private Set<ListenerOptions> options = Sets.newHashSet();
|
||||
|
||||
/**
|
||||
* Construct a new listening whitelist template.
|
||||
*
|
||||
* @param template - the template.
|
||||
*/
|
||||
private Builder(ListeningWhitelist template) {
|
||||
@@ -217,10 +311,9 @@ public class ListeningWhitelist {
|
||||
options(template.getOptions());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the priority to use when constructing new whitelists.
|
||||
*
|
||||
* @param priority - the priority.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@@ -228,64 +321,70 @@ 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() {
|
||||
return priority(ListenerPriority.HIGH);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the whitelist of packet IDs to copy when constructing new whitelists.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #types(Collection)} instead.
|
||||
* @param whitelist - the whitelist of packets.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder whitelist(Collection<Integer> whitelist) {
|
||||
this.types = PacketRegistry.toPacketTypes(safeSet(whitelist));
|
||||
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.
|
||||
*/
|
||||
@@ -293,10 +392,9 @@ 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.
|
||||
*/
|
||||
@@ -304,10 +402,9 @@ 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.
|
||||
*/
|
||||
@@ -315,19 +412,16 @@ 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.
|
||||
*/
|
||||
@@ -335,10 +429,9 @@ 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.
|
||||
*/
|
||||
@@ -346,47 +439,42 @@ public class ListeningWhitelist {
|
||||
this.options = safeSet(options);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the options to copy when constructing new whitelists.
|
||||
*
|
||||
* @param serverOptions - the options array.
|
||||
* @param options - the options array.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder options(ListenerOptions[] serverOptions) {
|
||||
this.options = safeSet(Sets.newHashSet(serverOptions));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Options to merge into the current set of options.
|
||||
*
|
||||
* @param serverOptions - the options array.
|
||||
* @param options - 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.
|
||||
* @param options - 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() {
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.events;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public MonitorAdapter(Plugin plugin, ConnectionSide side) {
|
||||
initialize(plugin, side, getLogger(plugin));
|
||||
}
|
||||
|
||||
public MonitorAdapter(Plugin plugin, ConnectionSide side, Logger logger) {
|
||||
initialize(plugin, side, logger);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void initialize(Plugin plugin, ConnectionSide side, Logger logger) {
|
||||
this.plugin = plugin;
|
||||
|
||||
// Recover in case something goes wrong
|
||||
try {
|
||||
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();
|
||||
} catch (FieldAccessException e) {
|
||||
if (side.isForServer())
|
||||
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getRegistry().values(), GamePhase.BOTH);
|
||||
if (side.isForClient())
|
||||
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getRegistry().values(), GamePhase.BOTH);
|
||||
logger.log(Level.WARNING, "Defaulting to 1.3 packets.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getSendingWhitelist() {
|
||||
return sending;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getReceivingWhitelist() {
|
||||
return receiving;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,415 @@
|
||||
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 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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@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;
|
||||
private final PacketType type;
|
||||
|
||||
// Cache serializer too
|
||||
private StreamSerializer serializer;
|
||||
|
||||
/**
|
||||
* Construct a new network marker.
|
||||
* @param side - whether or not this marker belongs to a client or server packet.
|
||||
* @param inputBuffer - the read serialized packet data.
|
||||
*/
|
||||
public NetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer, PacketType type) {
|
||||
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
|
||||
this.inputBuffer = inputBuffer;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new network marker.
|
||||
* <p>
|
||||
* The input buffer is only non-null for client-side packets.
|
||||
* @param side - whether or not this marker belongs to a client or server packet.
|
||||
* @param inputBuffer - the read serialized packet data.
|
||||
* @param handler - handle skipping headers.
|
||||
*/
|
||||
public NetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer, 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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) {
|
||||
return marker.scheduledPackets;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,639 @@
|
||||
/*
|
||||
* 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.events;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param params - the parameters.
|
||||
*/
|
||||
public PacketAdapter(@Nonnull AdapterParameteters params) {
|
||||
this(
|
||||
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 listenerPriority - the priority.
|
||||
* @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.
|
||||
*/
|
||||
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 listenerPriority - the priority.
|
||||
* @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 listenerPriority - the priority.
|
||||
* @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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener with the given parameters.
|
||||
* @param plugin - the plugin.
|
||||
* @param listenerPriority - the priority.
|
||||
* @param types - the packet types.
|
||||
*/
|
||||
public PacketAdapter(Plugin plugin, ListenerPriority listenerPriority, PacketType... types) {
|
||||
this(params(plugin, types).listenerPriority(listenerPriority));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener with default priority.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, Integer... packets) {
|
||||
this(plugin, connectionSide, ListenerPriority.NORMAL, packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, Set<Integer> packets) {
|
||||
this(plugin, connectionSide, listenerPriority, GamePhase.PLAYING, packets.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* The game phase is used to optimize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param gamePhase - which game phase this listener is active under.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, GamePhase gamePhase, Set<Integer> packets) {
|
||||
this(plugin, connectionSide, ListenerPriority.NORMAL, gamePhase, packets.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* The game phase is used to optimize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param gamePhase - which game phase this listener is active under.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, GamePhase gamePhase, Set<Integer> packets) {
|
||||
this(plugin, connectionSide, listenerPriority, gamePhase, packets.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, Integer... packets) {
|
||||
this(plugin, connectionSide, listenerPriority, GamePhase.PLAYING, packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param options - which listener options to use.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerOptions[] options, Integer... packets) {
|
||||
this(plugin, connectionSide, ListenerPriority.NORMAL, GamePhase.PLAYING, options, packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param gamePhase - which game phase this listener is active under.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, GamePhase gamePhase, Integer... packets) {
|
||||
this(plugin, connectionSide, ListenerPriority.NORMAL, gamePhase, packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* The game phase is used to optimize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param gamePhase - which game phase this listener is active under.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, GamePhase gamePhase, Integer... packets) {
|
||||
this(plugin, connectionSide, listenerPriority, gamePhase, new ListenerOptions[0], packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* The game phase is used to optimize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||
* <p>
|
||||
* Listener options must be specified in order for {@link NetworkMarker#getInputBuffer()} to function correctly.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param gamePhase - which game phase this listener is active under.
|
||||
* @param options - which listener options to use.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(
|
||||
Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority,
|
||||
GamePhase gamePhase, ListenerOptions[] options, Integer... packets) {
|
||||
|
||||
this(plugin, connectionSide, listenerPriority, gamePhase, options,
|
||||
PacketRegistry.toPacketTypes(Sets.newHashSet(packets), connectionSide.getSender()).toArray(new PacketType[0])
|
||||
);
|
||||
}
|
||||
|
||||
// For internal use only
|
||||
private PacketAdapter(
|
||||
Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority,
|
||||
GamePhase gamePhase, ListenerOptions[] options, PacketType... packets) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
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) {
|
||||
// Try to get the plugin name
|
||||
try {
|
||||
if (plugin == null)
|
||||
return "UNKNOWN";
|
||||
else
|
||||
return plugin.getName();
|
||||
|
||||
} catch (NoSuchMethodError e) {
|
||||
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() {
|
||||
return new AdapterParameteters();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Deprecated: Use {@link #params(Plugin, PacketType...)} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
* @return Helper object.
|
||||
*/
|
||||
@Deprecated
|
||||
public static AdapterParameteters params(Plugin plugin, Integer... packets) {
|
||||
return new AdapterParameteters().plugin(plugin).packets(packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public AdapterParameteters plugin(@Nonnull Plugin plugin) {
|
||||
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.
|
||||
*/
|
||||
public AdapterParameteters connectionSide(@Nonnull ConnectionSide connectionSide) {
|
||||
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.
|
||||
*/
|
||||
public AdapterParameteters listenerPriority(@Nonnull ListenerPriority listenerPriority) {
|
||||
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.
|
||||
* <p>
|
||||
* Default is {@link GamePhase#PLAYING}, which will not intercept login packets.
|
||||
* @param gamePhase - the new game phase.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public AdapterParameteters gamePhase(@Nonnull GamePhase gamePhase) {
|
||||
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.
|
||||
* <p>
|
||||
* The default is to disable this raw packet interception.
|
||||
* @param options - every option to use.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public AdapterParameteters options(@Nonnull ListenerOptions... options) {
|
||||
this.options = Preconditions.checkNotNull(options, "options cannot be NULL.");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public AdapterParameteters options(@Nonnull Set<? extends ListenerOptions> options) {
|
||||
Preconditions.checkNotNull(options, "options cannot be NULL.");
|
||||
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.
|
||||
*/
|
||||
private AdapterParameteters addOption(ListenerOptions option) {
|
||||
if (options == null) {
|
||||
return options(option);
|
||||
} else {
|
||||
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#DISABLE_GAMEPHASE_DETECTION}, causing ProtocolLib to ignore automatic game phase detection.
|
||||
* <p>
|
||||
* This is no longer relevant in 1.7.2.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public AdapterParameteters optionManualGamePhase() {
|
||||
return addOption(ListenerOptions.DISABLE_GAMEPHASE_DETECTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener option to {@link ListenerOptions#ASYNC}, indicating that our 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 packet IDs of the packets the listener is looking for.
|
||||
* <p>
|
||||
* This parameter is required.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #types(PacketType...)} instead.
|
||||
* @param packets - the packet IDs to look for.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@Deprecated
|
||||
public AdapterParameteters packets(@Nonnull Integer... packets) {
|
||||
Preconditions.checkNotNull(packets, "packets cannot be NULL");
|
||||
PacketType[] types = new PacketType[packets.length];
|
||||
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
types[i] = PacketType.findLegacy(packets[i]);
|
||||
}
|
||||
this.packets = types;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packet IDs of the packets the listener is looking for.
|
||||
* <p>
|
||||
* This parameter is required.
|
||||
* @param packets - a set of the packet IDs to look for.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@Deprecated
|
||||
public AdapterParameteters packets(@Nonnull Set<Integer> packets) {
|
||||
return packets(packets.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public AdapterParameteters types(@Nonnull PacketType... packets) {
|
||||
// Set the connection side as well
|
||||
if (connectionSide == null) {
|
||||
for (PacketType type : packets) {
|
||||
this.connectionSide = ConnectionSide.add(this.connectionSide, type.getSender().toSide());
|
||||
}
|
||||
}
|
||||
this.packets = Preconditions.checkNotNull(packets, "packets cannot be NULL");
|
||||
|
||||
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.
|
||||
*/
|
||||
public AdapterParameteters types(@Nonnull Set<PacketType> packets) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,864 @@
|
||||
/*
|
||||
* 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.events;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.util.com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.util.io.netty.buffer.UnpooledByteBufAllocator;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.WorldType;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.injector.StructureCache;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.ObjectWriter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.cloning.AggregateCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters;
|
||||
import com.comphenix.protocol.reflect.cloning.BukkitCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.Cloner;
|
||||
import com.comphenix.protocol.reflect.cloning.CollectionCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.FieldCloner;
|
||||
import com.comphenix.protocol.reflect.cloning.ImmutableDetector;
|
||||
import com.comphenix.protocol.reflect.cloning.SerializableCloner;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.StreamSerializer;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.ChunkCoordIntPair;
|
||||
import com.comphenix.protocol.wrappers.ChunkPosition;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.ChatVisibility;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.ClientCommand;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Difficulty;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
|
||||
import com.comphenix.protocol.wrappers.WrappedAttribute;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedServerPing;
|
||||
import com.comphenix.protocol.wrappers.WrappedStatistic;
|
||||
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtBase;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Represents a Minecraft packet indirectly.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PacketContainer implements Serializable {
|
||||
private static final long serialVersionUID = 3;
|
||||
|
||||
protected PacketType type;
|
||||
protected transient Object handle;
|
||||
|
||||
// Current structure modifier
|
||||
protected transient StructureModifier<Object> structureModifier;
|
||||
|
||||
// Support for serialization
|
||||
private static ConcurrentMap<Class<?>, Method> writeMethods = Maps.newConcurrentMap();
|
||||
private static ConcurrentMap<Class<?>, Method> readMethods = Maps.newConcurrentMap();
|
||||
|
||||
// Used to clone packets
|
||||
private static final AggregateCloner DEEP_CLONER = AggregateCloner.newBuilder().
|
||||
instanceProvider(DefaultInstances.DEFAULT).
|
||||
andThen(BukkitCloner.class).
|
||||
andThen(ImmutableDetector.class).
|
||||
andThen(CollectionCloner.class).
|
||||
andThen(getSpecializedDeepClonerFactory()).
|
||||
build();
|
||||
|
||||
private static final AggregateCloner SHALLOW_CLONER = AggregateCloner.newBuilder().
|
||||
instanceProvider(DefaultInstances.DEFAULT).
|
||||
andThen(new Function<BuilderParameters, Cloner>() {
|
||||
@Override
|
||||
public Cloner apply(@Nullable BuilderParameters param) {
|
||||
if (param == null)
|
||||
throw new IllegalArgumentException("Cannot be NULL.");
|
||||
|
||||
return new FieldCloner(param.getAggregateCloner(), param.getInstanceProvider()) {{
|
||||
// Use a default writer with no concept of cloning
|
||||
writer = new ObjectWriter();
|
||||
}};
|
||||
}
|
||||
}).
|
||||
build();
|
||||
|
||||
// Packets that cannot be cloned by our default deep cloner
|
||||
private static final Set<PacketType> CLONING_UNSUPPORTED = Sets.newHashSet(
|
||||
PacketType.Play.Server.UPDATE_ATTRIBUTES, PacketType.Status.Server.OUT_SERVER_INFO);
|
||||
|
||||
/**
|
||||
* Creates a packet container for a new packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #PacketContainer(PacketType)} instead.
|
||||
* @param id - ID of the packet to create.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketContainer(int id) {
|
||||
this(PacketType.findLegacy(id), StructureCache.newPacket(PacketType.findLegacy(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet container for an existing packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #PacketContainer(PacketType, Object))} instead.
|
||||
* @param id - ID of the given packet.
|
||||
* @param handle - contained packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketContainer(int id, Object handle) {
|
||||
this(PacketType.findLegacy(id), handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet container for an existing packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #PacketContainer(PacketType, Object, StructureModifier))} instead.
|
||||
* @param id - ID of the given packet.
|
||||
* @param handle - contained packet.
|
||||
* @param structure - structure modifier.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketContainer(int id, Object handle, StructureModifier<Object> structure) {
|
||||
this(PacketType.findLegacy(id), handle, structure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet container for a new packet.
|
||||
* @param type - the type of the packet to create.
|
||||
*/
|
||||
public PacketContainer(PacketType type) {
|
||||
this(type, StructureCache.newPacket(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet container for an existing packet.
|
||||
* @param id - ID of the given packet.
|
||||
* @param handle - contained packet.
|
||||
*/
|
||||
public PacketContainer(PacketType type, Object handle) {
|
||||
this(type, handle, StructureCache.getStructure(type).withTarget(handle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet container for an existing packet.
|
||||
* @param id - ID of the given packet.
|
||||
* @param handle - contained packet.
|
||||
* @param structure - structure modifier.
|
||||
*/
|
||||
public PacketContainer(PacketType type, Object handle, StructureModifier<Object> structure) {
|
||||
if (handle == null)
|
||||
throw new IllegalArgumentException("handle cannot be null.");
|
||||
if (type == null)
|
||||
throw new IllegalArgumentException("type cannot be null.");
|
||||
|
||||
this.type = type;
|
||||
this.handle = handle;
|
||||
this.structureModifier = structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new packet container from a given handle.
|
||||
* @param packet - the NMS packet.
|
||||
* @return The packet container.
|
||||
*/
|
||||
public static PacketContainer fromPacket(Object packet) {
|
||||
PacketType type = PacketType.fromClass(packet.getClass());
|
||||
return new PacketContainer(type, packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* For serialization.
|
||||
*/
|
||||
protected PacketContainer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the underlying Minecraft packet.
|
||||
* @return Underlying Minecraft packet.
|
||||
*/
|
||||
public Object getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the generic structure modifier for this packet.
|
||||
* @return Structure modifier.
|
||||
*/
|
||||
public StructureModifier<Object> getModifier() {
|
||||
return structureModifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for every field with the given type.
|
||||
* @param primitiveType - the type to find.
|
||||
* @return A modifier for this specific type.
|
||||
*/
|
||||
public <T> StructureModifier<T> getSpecificModifier(Class<T> primitiveType) {
|
||||
return structureModifier.withType(primitiveType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for every byte field.
|
||||
* @return A modifier for every byte field.
|
||||
*/
|
||||
public StructureModifier<Byte> getBytes() {
|
||||
return structureModifier.withType(byte.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for every boolean field.
|
||||
* @return A modifier for every boolean field.
|
||||
*/
|
||||
public StructureModifier<Boolean> getBooleans() {
|
||||
return structureModifier.withType(boolean.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for every short field.
|
||||
* @return A modifier for every short field.
|
||||
*/
|
||||
public StructureModifier<Short> getShorts() {
|
||||
return structureModifier.withType(short.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for every integer field.
|
||||
* @return A modifier for every integer field.
|
||||
*/
|
||||
public StructureModifier<Integer> getIntegers() {
|
||||
return structureModifier.withType(int.class);
|
||||
}
|
||||
/**
|
||||
* Retrieves a read/write structure for every long field.
|
||||
* @return A modifier for every long field.
|
||||
*/
|
||||
public StructureModifier<Long> getLongs() {
|
||||
return structureModifier.withType(long.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for every float field.
|
||||
* @return A modifier for every float field.
|
||||
*/
|
||||
public StructureModifier<Float> getFloat() {
|
||||
return structureModifier.withType(float.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for every double field.
|
||||
* @return A modifier for every double field.
|
||||
*/
|
||||
public StructureModifier<Double> getDoubles() {
|
||||
return structureModifier.withType(double.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for every String field.
|
||||
* @return A modifier for every String field.
|
||||
*/
|
||||
public StructureModifier<String> getStrings() {
|
||||
return structureModifier.withType(String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for every String array field.
|
||||
* @return A modifier for every String array field.
|
||||
*/
|
||||
public StructureModifier<String[]> getStringArrays() {
|
||||
return structureModifier.withType(String[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for every byte array field.
|
||||
* @return A modifier for every byte array field.
|
||||
*/
|
||||
public StructureModifier<byte[]> getByteArrays() {
|
||||
return structureModifier.withType(byte[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a serializer for reading and writing ItemStacks stored in a byte array.
|
||||
* @return A instance of the serializer.
|
||||
*/
|
||||
public StreamSerializer getByteArraySerializer() {
|
||||
return new StreamSerializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for every int array field.
|
||||
* @return A modifier for every int array field.
|
||||
*/
|
||||
public StructureModifier<int[]> getIntegerArrays() {
|
||||
return structureModifier.withType(int[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for ItemStack.
|
||||
* <p>
|
||||
* This modifier will automatically marshall between the Bukkit ItemStack and the
|
||||
* internal Minecraft ItemStack.
|
||||
* @return A modifier for ItemStack fields.
|
||||
*/
|
||||
public StructureModifier<ItemStack> getItemModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<ItemStack>withType(
|
||||
MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for arrays of ItemStacks.
|
||||
* <p>
|
||||
* This modifier will automatically marshall between the Bukkit ItemStack and the
|
||||
* internal Minecraft ItemStack.
|
||||
* @return A modifier for ItemStack array fields.
|
||||
*/
|
||||
public StructureModifier<ItemStack[]> getItemArrayModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<ItemStack[]>withType(
|
||||
MinecraftReflection.getItemStackArrayClass(),
|
||||
BukkitConverters.getIgnoreNull(new ItemStackArrayConverter()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for maps of statistics.
|
||||
* <p>
|
||||
* Note that you must write back the changed map to persist it.
|
||||
* @return A modifier for maps of statistics.
|
||||
*/
|
||||
public StructureModifier<Map<WrappedStatistic, Integer>> getStatisticMaps() {
|
||||
return structureModifier.withType(Map.class,
|
||||
BukkitConverters.<WrappedStatistic, Integer>getMapConverter(
|
||||
MinecraftReflection.getStatisticClass(),
|
||||
BukkitConverters.getWrappedStatisticConverter()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for the world type enum.
|
||||
* <p>
|
||||
* This modifier will automatically marshall between the Bukkit world type and the
|
||||
* internal Minecraft world type.
|
||||
* @return A modifier for world type fields.
|
||||
*/
|
||||
public StructureModifier<WorldType> getWorldTypeModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<WorldType>withType(
|
||||
MinecraftReflection.getWorldTypeClass(),
|
||||
BukkitConverters.getWorldTypeConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for data watchers.
|
||||
* @return A modifier for data watchers.
|
||||
*/
|
||||
public StructureModifier<WrappedDataWatcher> getDataWatcherModifier() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<WrappedDataWatcher>withType(
|
||||
MinecraftReflection.getDataWatcherClass(),
|
||||
BukkitConverters.getDataWatcherConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for entity objects.
|
||||
* <p>
|
||||
* Note that entities are transmitted by integer ID, and the type may not be enough
|
||||
* to distinguish between entities and other values. Thus, this structure modifier
|
||||
* MAY return null or invalid entities for certain fields. Using the correct index
|
||||
* is essential.
|
||||
*
|
||||
* @param world - the world each entity is currently occupying.
|
||||
* @return A modifier entity types.
|
||||
*/
|
||||
public StructureModifier<Entity> getEntityModifier(@Nonnull World world) {
|
||||
Preconditions.checkNotNull(world, "world cannot be NULL.");
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<Entity>withType(
|
||||
int.class, BukkitConverters.getEntityConverter(world));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for entity objects.
|
||||
* <p>
|
||||
* Note that entities are transmitted by integer ID, and the type may not be enough
|
||||
* to distinguish between entities and other values. Thus, this structure modifier
|
||||
* MAY return null or invalid entities for certain fields. Using the correct index
|
||||
* is essential.
|
||||
*
|
||||
* @param event - the original packet event.
|
||||
* @return A modifier entity types.
|
||||
*/
|
||||
public StructureModifier<Entity> getEntityModifier(@Nonnull PacketEvent event) {
|
||||
Preconditions.checkNotNull(event, "event cannot be NULL.");
|
||||
return getEntityModifier(event.getPlayer().getWorld());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 wrapped ChunkCoordIntPairs.
|
||||
* @return A modifier for ChunkCoordIntPair.
|
||||
*/
|
||||
public StructureModifier<ChunkCoordIntPair> getChunkCoordIntPairs() {
|
||||
// Allow access to the NBT class in packet 130
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getChunkCoordIntPair(),
|
||||
ChunkCoordIntPair.getConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for NBT classes.
|
||||
* @return A modifier for NBT classes.
|
||||
*/
|
||||
public StructureModifier<NbtBase<?>> getNbtModifier() {
|
||||
// Allow access to the NBT class in packet 130
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getNBTBaseClass(),
|
||||
BukkitConverters.getNbtConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for collections of attribute snapshots.
|
||||
* <p>
|
||||
* This modifier will automatically marshall between the visible ProtocolLib WrappedAttribute and the
|
||||
* internal Minecraft AttributeSnapshot.
|
||||
* @return A modifier for AttributeSnapshot collection fields.
|
||||
*/
|
||||
public StructureModifier<List<WrappedAttribute>> getAttributeCollectionModifier() {
|
||||
// Convert to and from the ProtocolLib wrapper
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(
|
||||
MinecraftReflection.getAttributeSnapshotClass(),
|
||||
BukkitConverters.getWrappedAttributeConverter())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for collections of chunk positions.
|
||||
* <p>
|
||||
* This modifier will automatically marshall 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(
|
||||
MinecraftReflection.getChunkPositionClass(),
|
||||
ChunkPosition.getConverter())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for collections of watchable objects.
|
||||
* <p>
|
||||
* This modifier will automatically marshall between the visible WrappedWatchableObject and the
|
||||
* internal Minecraft WatchableObject.
|
||||
* @return A modifier for watchable object list fields.
|
||||
*/
|
||||
public StructureModifier<List<WrappedWatchableObject>> getWatchableCollectionModifier() {
|
||||
// Convert to and from the ProtocolLib wrapper
|
||||
return structureModifier.withType(
|
||||
Collection.class,
|
||||
BukkitConverters.getListConverter(
|
||||
MinecraftReflection.getWatchableObjectClass(),
|
||||
BukkitConverters.getWatchableObjectConverter())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for block fields.
|
||||
* <p>
|
||||
* This modifier will automatically marshall between Material and the
|
||||
* internal Minecraft Block.
|
||||
* @return A modifier for GameProfile fields.
|
||||
*/
|
||||
public StructureModifier<Material> getBlocks() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<Material>withType(
|
||||
MinecraftReflection.getBlockClass(), BukkitConverters.getBlockConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for game profiles in Minecraft 1.7.2.
|
||||
* <p>
|
||||
* This modifier will automatically marshall between WrappedGameProfile and the
|
||||
* internal Minecraft GameProfile.
|
||||
* @return A modifier for GameProfile fields.
|
||||
*/
|
||||
public StructureModifier<WrappedGameProfile> getGameProfiles() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<WrappedGameProfile>withType(
|
||||
GameProfile.class, BukkitConverters.getWrappedGameProfileConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for chat components in Minecraft 1.7.2.
|
||||
* <p>
|
||||
* This modifier will automatically marshall between WrappedChatComponent and the
|
||||
* internal Minecraft GameProfile.
|
||||
* @return A modifier for ChatComponent fields.
|
||||
*/
|
||||
public StructureModifier<WrappedChatComponent> getChatComponents() {
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<WrappedChatComponent>withType(
|
||||
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the ServerPing fields in the following packet: <br>
|
||||
* <ul>
|
||||
* <li>{@link PacketType.Status.Server#OUT_SERVER_INFO}
|
||||
* </ul>
|
||||
* @return A modifier for ServerPing fields.
|
||||
*/
|
||||
public StructureModifier<WrappedServerPing> getServerPings() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<WrappedServerPing>withType(
|
||||
MinecraftReflection.getServerPingClass(), BukkitConverters.getWrappedServerPingConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the Protocol enum in 1.7.2.
|
||||
* @return A modifier for Protocol enum fields.
|
||||
*/
|
||||
public StructureModifier<Protocol> getProtocols() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<Protocol>withType(
|
||||
EnumWrappers.getProtocolClass(), EnumWrappers.getProtocolConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the ClientCommand enum in 1.7.2.
|
||||
* @return A modifier for ClientCommand enum fields.
|
||||
*/
|
||||
public StructureModifier<ClientCommand> getClientCommands() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<ClientCommand>withType(
|
||||
EnumWrappers.getClientCommandClass(), EnumWrappers.getClientCommandConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the ChatVisibility enum in 1.7.2.
|
||||
* @return A modifier for ChatVisibility enum fields.
|
||||
*/
|
||||
public StructureModifier<ChatVisibility> getChatVisibilities() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<ChatVisibility>withType(
|
||||
EnumWrappers.getChatVisibilityClass(), EnumWrappers.getChatVisibilityConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the Difficulty enum in 1.7.2.
|
||||
* @return A modifier for Difficulty enum fields.
|
||||
*/
|
||||
public StructureModifier<Difficulty> getDifficulties() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<Difficulty>withType(
|
||||
EnumWrappers.getDifficultyClass(), EnumWrappers.getDifficultyConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the EntityUse enum in 1.7.2.
|
||||
* @return A modifier for EntityUse enum fields.
|
||||
*/
|
||||
public StructureModifier<EntityUseAction> getEntityUseActions() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<EntityUseAction>withType(
|
||||
EnumWrappers.getEntityUseActionClass(), EnumWrappers.getEntityUseActionConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the NativeGameMode enum in 1.7.2.
|
||||
* @return A modifier for NativeGameMode enum fields.
|
||||
*/
|
||||
public StructureModifier<NativeGameMode> getGameModes() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.<NativeGameMode>withType(
|
||||
EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ID of this packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getType()} instead.
|
||||
* @return Packet ID.
|
||||
*/
|
||||
@Deprecated
|
||||
public int getID() {
|
||||
return type.getLegacyId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the packet type of this packet.
|
||||
* @return The packet type.
|
||||
*/
|
||||
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>
|
||||
* This will perform a full copy of the entire object tree, only skipping
|
||||
* 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;
|
||||
|
||||
// Fall back on the alternative (but slower) method of reading and writing back the packet
|
||||
if (CLONING_UNSUPPORTED.contains(type)) {
|
||||
clonedPacket = SerializableCloner.clone(this).getHandle();
|
||||
} else {
|
||||
clonedPacket = DEEP_CLONER.clone(getHandle());
|
||||
}
|
||||
return new PacketContainer(getType(), clonedPacket);
|
||||
}
|
||||
|
||||
// To save space, we'll skip copying the inflated buffers in packet 51 and 56
|
||||
private static Function<BuilderParameters, Cloner> getSpecializedDeepClonerFactory() {
|
||||
// Look at what you've made me do Java, look at it!!
|
||||
return new Function<BuilderParameters, Cloner>() {
|
||||
@Override
|
||||
public Cloner apply(@Nullable BuilderParameters param) {
|
||||
return new FieldCloner(param.getAggregateCloner(), param.getInstanceProvider()) {{
|
||||
this.writer = new ObjectWriter() {
|
||||
protected void transformField(StructureModifier<Object> modifierSource,
|
||||
StructureModifier<Object> modifierDest, int fieldIndex) {
|
||||
// No need to clone inflated buffers
|
||||
if (modifierSource.getField(fieldIndex).getName().startsWith("inflatedBuffer"))
|
||||
modifierDest.write(fieldIndex, modifierSource.read(fieldIndex));
|
||||
else
|
||||
defaultTransform(modifierSource, modifierDest, getDefaultCloner(), fieldIndex);
|
||||
};
|
||||
};
|
||||
}};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream output) throws IOException {
|
||||
// Default serialization
|
||||
output.defaultWriteObject();
|
||||
|
||||
// We'll take care of NULL packets as well
|
||||
output.writeBoolean(handle != null);
|
||||
|
||||
try {
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
ByteBuf buffer = createPacketBuffer();
|
||||
MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, buffer);
|
||||
|
||||
output.writeInt(buffer.readableBytes());
|
||||
buffer.readBytes(output, buffer.readableBytes());
|
||||
|
||||
} else {
|
||||
// Call the write-method
|
||||
output.writeInt(-1);
|
||||
getMethodLazily(writeMethods, handle.getClass(), "write", DataOutput.class).
|
||||
invoke(handle, new DataOutputStream(output));
|
||||
}
|
||||
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
|
||||
// Default deserialization
|
||||
input.defaultReadObject();
|
||||
|
||||
// Get structure modifier
|
||||
structureModifier = StructureCache.getStructure(type);
|
||||
|
||||
// Don't read NULL packets
|
||||
if (input.readBoolean()) {
|
||||
|
||||
// Create a default instance of the packet
|
||||
handle = StructureCache.newPacket(type);
|
||||
|
||||
// Call the read method
|
||||
try {
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
ByteBuf buffer = createPacketBuffer();
|
||||
buffer.writeBytes(input, input.readInt());
|
||||
|
||||
MinecraftMethods.getPacketReadByteBufMethod().invoke(handle, buffer);
|
||||
} else {
|
||||
if (input.readInt() != -1)
|
||||
throw new IllegalArgumentException("Cannot load a packet from 1.7.2 in 1.6.4.");
|
||||
|
||||
getMethodLazily(readMethods, handle.getClass(), "read", DataInput.class).
|
||||
invoke(handle, new DataInputStream(input));
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
|
||||
// And we're done
|
||||
structureModifier = structureModifier.withTarget(handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new packet data serializer.
|
||||
* @return The packet data serializer.
|
||||
*/
|
||||
private ByteBuf createPacketBuffer() {
|
||||
return MinecraftReflection.getPacketDataSerializer(UnpooledByteBufAllocator.DEFAULT.buffer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cached method concurrently.
|
||||
* @param lookup - a lazy lookup cache.
|
||||
* @param handleClass - class type of the current packet.
|
||||
* @param methodName - name of method to retrieve.
|
||||
* @param parameterClass - the one parameter type in the method.
|
||||
* @return Reflected method.
|
||||
*/
|
||||
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(
|
||||
FuzzyMethodContract.newBuilder().
|
||||
parameterCount(1).
|
||||
parameterDerivedOf(parameterClass).
|
||||
returnTypeVoid().
|
||||
build());
|
||||
method = lookup.putIfAbsent(handleClass, initialized);
|
||||
|
||||
// Use our version if we succeeded
|
||||
if (method == null) {
|
||||
method = initialized;
|
||||
}
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an equivalent converter for ItemStack arrays.
|
||||
* @author Kristian
|
||||
*/
|
||||
private static class ItemStackArrayConverter implements EquivalentConverter<ItemStack[]> {
|
||||
final EquivalentConverter<ItemStack> stackConverter = BukkitConverters.getItemStackConverter();
|
||||
|
||||
public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
|
||||
Class<?> nmsStack = MinecraftReflection.getItemStackClass();
|
||||
Object[] result = (Object[]) Array.newInstance(nmsStack, specific.length);
|
||||
|
||||
// Unwrap every item
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = stackConverter.getGeneric(nmsStack, specific[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getSpecific(Object generic) {
|
||||
Object[] input = (Object[]) generic;
|
||||
ItemStack[] result = new ItemStack[input.length];
|
||||
|
||||
// Add the wrapper
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = stackConverter.getSpecific(input[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<ItemStack[]> getSpecificType() {
|
||||
return ItemStack[].class;
|
||||
}
|
||||
}
|
||||
}
|
||||
+129
-241
@@ -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
|
||||
*/
|
||||
|
||||
@@ -23,61 +23,57 @@ import java.io.ObjectOutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.EventObject;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
|
||||
import com.comphenix.protocol.Application;
|
||||
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.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;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* 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());
|
||||
|
||||
|
||||
private static final SetMultimap<PacketType, PacketType> CHANGE_WARNINGS =
|
||||
Multimaps.synchronizedSetMultimap(HashMultimap.<PacketType, PacketType>create());
|
||||
|
||||
/**
|
||||
* Automatically generated by Eclipse.
|
||||
*/
|
||||
private static final long serialVersionUID = -5360289379097430620L;
|
||||
// Network input and output handlers
|
||||
NetworkMarker networkMarker;
|
||||
|
||||
private transient WeakReference<Player> playerReference;
|
||||
private transient Player offlinePlayer;
|
||||
|
||||
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;
|
||||
|
||||
@Nullable
|
||||
private PacketEvent bundle;
|
||||
|
||||
|
||||
/**
|
||||
* Use the static constructors to create instances of this event.
|
||||
*
|
||||
* @param source - the event source.
|
||||
*/
|
||||
public PacketEvent(Object source) {
|
||||
@@ -86,38 +82,32 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
}
|
||||
|
||||
private PacketEvent(Object source, PacketContainer packet, Player player, boolean serverPacket) {
|
||||
this(source, packet, null, player, serverPacket, true, null);
|
||||
this(source, packet, null, player, serverPacket, true);
|
||||
}
|
||||
|
||||
private PacketEvent(Object source, PacketContainer packet, NetworkMarker marker, Player player, boolean serverPacket,
|
||||
boolean filtered, @Nullable PacketEvent bundleEvent) {
|
||||
|
||||
private PacketEvent(Object source, PacketContainer packet, NetworkMarker marker, Player player, boolean serverPacket, boolean filtered) {
|
||||
super(source);
|
||||
if(packet == null) {
|
||||
throw new IllegalArgumentException("packet cannot be null");
|
||||
}
|
||||
this.packet = packet;
|
||||
this.playerReference = new WeakReference<>(player);
|
||||
this.playerReference = new WeakReference<Player>(player);
|
||||
this.networkMarker = marker;
|
||||
this.serverPacket = serverPacket;
|
||||
this.filtered = filtered;
|
||||
this.bundle = bundleEvent;
|
||||
}
|
||||
|
||||
private PacketEvent(PacketEvent original, AsyncMarker asyncMarker) {
|
||||
super(original.source);
|
||||
this.packet = original.packet;
|
||||
this.playerReference = original.getPlayerReference();
|
||||
this.cancel = original.cancel;
|
||||
this.serverPacket = original.serverPacket;
|
||||
this.filtered = original.filtered;
|
||||
this.networkMarker = original.networkMarker;
|
||||
|
||||
private PacketEvent(PacketEvent origial, AsyncMarker asyncMarker) {
|
||||
super(origial.source);
|
||||
this.packet = origial.packet;
|
||||
this.playerReference = origial.playerReference;
|
||||
this.cancel = origial.cancel;
|
||||
this.serverPacket = origial.serverPacket;
|
||||
this.filtered = origial.filtered;
|
||||
this.networkMarker = origial.networkMarker;
|
||||
this.asyncMarker = asyncMarker;
|
||||
this.asynchronous = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -126,10 +116,9 @@ 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.
|
||||
@@ -137,114 +126,87 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
* @return The event.
|
||||
*/
|
||||
public static PacketEvent fromClient(Object source, PacketContainer packet, NetworkMarker marker, Player client) {
|
||||
return new PacketEvent(source, packet, marker, client, false, true, null);
|
||||
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) {
|
||||
return new PacketEvent(source, packet, marker, client, false, filtered, null);
|
||||
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, null);
|
||||
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) {
|
||||
return fromServer(source, packet, marker, recipient, filtered, null);
|
||||
public static PacketEvent fromServer(Object source, PacketContainer packet, NetworkMarker marker, Player recipient, boolean filtered) {
|
||||
return new PacketEvent(source, packet, marker, recipient, true, filtered);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 recipient - the client that will receieve the packet.
|
||||
* @param filtered - whether this packet event is processed by every packet listener.
|
||||
* @param bundle - The corresponding packet event of the bundle if this packet is part of a bundle. Otherwise, null.
|
||||
* @return The event.
|
||||
*/
|
||||
public static PacketEvent fromServer(Object source, PacketContainer packet, NetworkMarker marker, Player recipient,
|
||||
boolean filtered, @Nullable PacketEvent bundle) {
|
||||
return new PacketEvent(source, packet, marker, recipient, true, filtered, bundle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
return !Application.isPrimaryThread();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the packet that will be sent to the player.
|
||||
*
|
||||
* @return Packet to send to the player.
|
||||
*/
|
||||
public PacketContainer getPacket() {
|
||||
@@ -253,96 +215,68 @@ 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();
|
||||
if (this.packet != null && !Objects.equal(oldType, newType)) {
|
||||
// Only report this once
|
||||
if (CHANGE_WARNINGS.put(oldType, newType)) {
|
||||
ProtocolLibrary.getErrorReporter().reportWarning(this,
|
||||
ProtocolLibrary.getErrorReporter().reportWarning(this,
|
||||
Report.newBuilder(REPORT_CHANGING_PACKET_TYPE_IS_CONFUSING).
|
||||
messageParam(PluginContext.getPluginCaller(new Exception()), oldType, newType).
|
||||
build());
|
||||
messageParam(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();
|
||||
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(serverPacket ? ConnectionSide.SERVER_SIDE : ConnectionSide.CLIENT_SIDE, this.getPacketType());
|
||||
networkMarker = new NetworkMarker.EmptyBufferMarker(
|
||||
serverPacket ? ConnectionSide.SERVER_SIDE : ConnectionSide.CLIENT_SIDE);
|
||||
} else {
|
||||
throw new IllegalStateException("Add the option ListenerOptions.INTERCEPT_INPUT_BUFFER to your listener.");
|
||||
}
|
||||
@@ -354,80 +288,59 @@ 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");
|
||||
}
|
||||
|
||||
private WeakReference<Player> getPlayerReference() {
|
||||
Player player = playerReference.get();
|
||||
|
||||
if (player instanceof TemporaryPlayer) {
|
||||
Player updated = player.getPlayer();
|
||||
if (updated != null && !(updated instanceof TemporaryPlayer)) {
|
||||
playerReference.clear();
|
||||
playerReference = new WeakReference<>(updated);
|
||||
}
|
||||
}
|
||||
|
||||
return playerReference;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void setCancelled(boolean cancel) {
|
||||
if (readOnly)
|
||||
throw new IllegalStateException("The packet event is read-only.");
|
||||
this.cancel = cancel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the player that has sent the packet or is receiving it.
|
||||
*
|
||||
* Retrieves the player that has sent the packet or is recieving it.
|
||||
* @return The player associated with this event.
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return getPlayerReference().get();
|
||||
return playerReference.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the player in this PacketEvent is temporary (i.e. they don't have a true player instance yet).
|
||||
* Temporary players have a limited subset of methods that may be used:
|
||||
* <ul>
|
||||
* <li>getPlayer</li>
|
||||
* <li>getAddress</li>
|
||||
* <li>getServer</li>
|
||||
* <li>chat</li>
|
||||
* <li>sendMessage</li>
|
||||
* <li>kickPlayer</li>
|
||||
* <li>isOnline</li>
|
||||
* </ul>
|
||||
* <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.
|
||||
*
|
||||
* @return True if the player is temporary, false if not.
|
||||
*/
|
||||
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>
|
||||
@@ -435,62 +348,54 @@ 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.
|
||||
* 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.
|
||||
*
|
||||
* 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() {
|
||||
@@ -501,16 +406,14 @@ 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.
|
||||
*/
|
||||
@@ -520,41 +423,26 @@ 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)
|
||||
Player player = getPlayer();
|
||||
output.writeObject(player != null ? new SerializedOfflinePlayer(player) : null);
|
||||
output.writeObject(playerReference.get() != null ? new SerializedOfflinePlayer(playerReference.get()) : null);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the packet event corresponding to the bundle if this packet is sent as a part of a bundle, t. Otherwise, null.
|
||||
* @return Corresponding packet event or null.
|
||||
*/
|
||||
@Nullable
|
||||
public PacketEvent getBundle() {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PacketEvent[player=" + getPlayer() + ", packet=" + packet + "]";
|
||||
if (serialized != null) {
|
||||
// Store it, to prevent weak reference from cleaning up the reference
|
||||
offlinePlayer = serialized.getPlayer();
|
||||
playerReference = new WeakReference<Player>(offlinePlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-27
@@ -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
|
||||
*/
|
||||
|
||||
@@ -23,53 +23,49 @@ 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 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.
|
||||
*
|
||||
* 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.
|
||||
* @param event - the packet that should be sent.
|
||||
*/
|
||||
void onPacketSending(PacketEvent event);
|
||||
public void onPacketSending(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Invoked right before a received packet from a client is being processed.
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <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.
|
||||
* @param event - the packet that has been received.
|
||||
*/
|
||||
void onPacketReceiving(PacketEvent event);
|
||||
|
||||
public 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.
|
||||
*/
|
||||
ListeningWhitelist getSendingWhitelist();
|
||||
|
||||
public ListeningWhitelist getSendingWhitelist();
|
||||
|
||||
/**
|
||||
* Retrieve which packets sent by the client this listener will observe.
|
||||
*
|
||||
* @return List of server packets to observe, along with the priority.
|
||||
*/
|
||||
ListeningWhitelist getReceivingWhitelist();
|
||||
|
||||
public ListeningWhitelist getReceivingWhitelist();
|
||||
|
||||
/**
|
||||
* Retrieve the plugin that created list packet listener.
|
||||
*
|
||||
* @return The plugin, or NULL if not available.
|
||||
*/
|
||||
Plugin getPlugin();
|
||||
public Plugin getPlugin();
|
||||
}
|
||||
File renamed without changes.
+1
-1
@@ -28,7 +28,7 @@ public interface PacketOutputHandler {
|
||||
* Note that the buffer is initially filled with the output from the default write method.
|
||||
* <p>
|
||||
* In Minecraft 1.6.4, the header is always excluded, whereas it MUST be included in Minecraft 1.7.2. Call
|
||||
* NetworkMarker#requireOutputHeader() to determine this.
|
||||
* {@link NetworkMarker#requireOutputHeader()} to determine this.
|
||||
* @param event - the packet that will be outputted.
|
||||
* @param buffer - the data that is currently scheduled to be outputted.
|
||||
* @return The modified byte array to write. NULL is not permitted.
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* Represents an adapter version of a post listener.
|
||||
* @author Kristian
|
||||
File renamed without changes.
+21
-8
@@ -1,12 +1,15 @@
|
||||
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.Objects;
|
||||
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
|
||||
@@ -119,16 +122,26 @@ public class ScheduledPacket {
|
||||
*/
|
||||
public void schedule(PacketStream stream) {
|
||||
Preconditions.checkNotNull(stream, "stream cannot be NULL");
|
||||
|
||||
if (getSender() == Sender.CLIENT) {
|
||||
stream.receiveClientPacket(getTarget(), getPacket(), isFiltered());
|
||||
} else {
|
||||
stream.sendServerPacket(getTarget(), getPacket(), isFiltered());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ScheduledPacket[packet=" + packet + ", target=" + target + ", filtered=" + filtered + "]";
|
||||
return Objects.toStringHelper(this)
|
||||
.add("packet", packet)
|
||||
.add("target", target)
|
||||
.add("filtered", filtered)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* 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.events;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
|
||||
/**
|
||||
* Represents a player object that can be serialized by Java.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
||||
|
||||
/**
|
||||
* Generated by Eclipse.
|
||||
*/
|
||||
private static final long serialVersionUID = -2728976288470282810L;
|
||||
|
||||
private transient Location bedSpawnLocation;
|
||||
|
||||
// Relevant data about an offline player
|
||||
private String name;
|
||||
private UUID uuid;
|
||||
private long firstPlayed;
|
||||
private long lastPlayed;
|
||||
private boolean operator;
|
||||
private boolean banned;
|
||||
private boolean playedBefore;
|
||||
private boolean online;
|
||||
private boolean whitelisted;
|
||||
|
||||
// Proxy helper
|
||||
private static Map<String, Method> lookup = new ConcurrentHashMap<String, Method>();
|
||||
|
||||
/**
|
||||
* Constructor used by serialization.
|
||||
*/
|
||||
public SerializedOfflinePlayer() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this serializable offline player from another player.
|
||||
* @param offline - another player.
|
||||
*/
|
||||
public SerializedOfflinePlayer(OfflinePlayer offline) {
|
||||
this.name = offline.getName();
|
||||
this.uuid = offline.getUniqueId();
|
||||
this.firstPlayed = offline.getFirstPlayed();
|
||||
this.lastPlayed = offline.getLastPlayed();
|
||||
this.operator = offline.isOp();
|
||||
this.banned = offline.isBanned();
|
||||
this.playedBefore = offline.hasPlayedBefore();
|
||||
this.online = offline.isOnline();
|
||||
this.whitelisted = offline.isWhitelisted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOp() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOp(boolean operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getBedSpawnLocation() {
|
||||
return bedSpawnLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFirstPlayed() {
|
||||
return firstPlayed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastPlayed() {
|
||||
return lastPlayed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPlayedBefore() {
|
||||
return playedBefore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBanned() {
|
||||
return banned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBanned(boolean banned) {
|
||||
this.banned = banned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return online;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted() {
|
||||
return whitelisted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhitelisted(boolean whitelisted) {
|
||||
this.whitelisted = whitelisted;
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream output) throws IOException {
|
||||
output.defaultWriteObject();
|
||||
|
||||
// Serialize the bed spawn location
|
||||
output.writeUTF(bedSpawnLocation.getWorld().getName());
|
||||
output.writeDouble(bedSpawnLocation.getX());
|
||||
output.writeDouble(bedSpawnLocation.getY());
|
||||
output.writeDouble(bedSpawnLocation.getZ());
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
|
||||
input.defaultReadObject();
|
||||
|
||||
// Well, this is a problem
|
||||
bedSpawnLocation = new Location(
|
||||
getWorld(input.readUTF()),
|
||||
input.readDouble(),
|
||||
input.readDouble(),
|
||||
input.readDouble()
|
||||
);
|
||||
}
|
||||
|
||||
private World getWorld(String name) {
|
||||
try {
|
||||
// Try to get the world at least
|
||||
return Bukkit.getServer().getWorld(name);
|
||||
} catch (Exception e) {
|
||||
// Screw it
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
try {
|
||||
// Try to get the real player underneath
|
||||
return Bukkit.getServer().getPlayerExact(name);
|
||||
} catch (Exception e) {
|
||||
return getProxyPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a player object that implements OfflinePlayer by refering to this object.
|
||||
* <p>
|
||||
* All other methods cause an exception.
|
||||
* @return Proxy object.
|
||||
*/
|
||||
public Player getProxyPlayer() {
|
||||
|
||||
// Remember to initialize the method filter
|
||||
if (lookup.size() == 0) {
|
||||
// Add all public methods
|
||||
for (Method method : OfflinePlayer.class.getMethods()) {
|
||||
lookup.put(method.getName(), method);
|
||||
}
|
||||
}
|
||||
|
||||
// MORE CGLIB magic!
|
||||
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
|
||||
ex.setSuperclass(Player.class);
|
||||
ex.setCallback(new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
|
||||
// There's no overloaded methods, so we don't care
|
||||
Method offlineMethod = lookup.get(method.getName());
|
||||
|
||||
// Ignore all other methods
|
||||
if (offlineMethod == null) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The method " + method.getName() + " is not supported for offline players.");
|
||||
}
|
||||
|
||||
// Invoke our on method
|
||||
return offlineMethod.invoke(SerializedOfflinePlayer.this, args);
|
||||
}
|
||||
});
|
||||
|
||||
return (Player) ex.create();
|
||||
}
|
||||
}
|
||||
+92
-137
@@ -2,179 +2,164 @@
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import 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 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.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.primitives.Primitives;
|
||||
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.
|
||||
* <p>
|
||||
* Typical conversions include:
|
||||
* <ul>
|
||||
* <li>org.bukkit.entity.Player to net.minecraft.server.EntityPlayer</li>
|
||||
* <li>org.bukkit.World to net.minecraft.server.WorldServer</li>
|
||||
* <li>org.bukkit.entity.Player -> net.minecraft.server.EntityPlayer</li>
|
||||
* <li>org.bukkit.World -> 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 final Map<Class<?>, Unwrapper> UNWRAPPER_CACHE = new ConcurrentHashMap<Class<?>, Unwrapper>();
|
||||
private static BukkitUnwrapper DEFAULT;
|
||||
|
||||
|
||||
private static Map<Class<?>, Unwrapper> unwrapperCache = new ConcurrentHashMap<Class<?>, Unwrapper>();
|
||||
|
||||
// The current error reporter
|
||||
private final ErrorReporter reporter;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@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 (UNWRAPPER_CACHE.containsKey(type)) {
|
||||
if (unwrapperCache.containsKey(type)) {
|
||||
// We will never remove from the cache, so this ought to be thread safe
|
||||
return UNWRAPPER_CACHE.get(type);
|
||||
return unwrapperCache.get(type);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
final Method find = type.getMethod("getHandle");
|
||||
|
||||
// It's thread safe, as getMethod should return the same handle
|
||||
|
||||
// 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,
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_ILLEGAL_ARGUMENT).error(e).callerParam(wrappedObject, find)
|
||||
);
|
||||
} catch (IllegalAccessException e) {
|
||||
@@ -184,105 +169,75 @@ public class BukkitUnwrapper implements Unwrapper {
|
||||
// This is really bad
|
||||
throw new RuntimeException("Minecraft error.", e);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
UNWRAPPER_CACHE.put(type, methodUnwrapper);
|
||||
|
||||
unwrapperCache.put(type, methodUnwrapper);
|
||||
return methodUnwrapper;
|
||||
|
||||
|
||||
} catch (SecurityException e) {
|
||||
reporter.reportDetailed(this,
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_SECURITY_LIMITATION).error(e).callerParam(type)
|
||||
);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// Maybe it's a proxy?
|
||||
Unwrapper proxyUnwrapper = getProxyUnwrapper(type);
|
||||
if (proxyUnwrapper != null) {
|
||||
return proxyUnwrapper;
|
||||
}
|
||||
|
||||
// Try getting the field unwrapper too
|
||||
Unwrapper fieldUnwrapper = getFieldUnwrapper(type);
|
||||
if (fieldUnwrapper != null) {
|
||||
|
||||
if (fieldUnwrapper != null)
|
||||
return fieldUnwrapper;
|
||||
} else {
|
||||
reporter.reportDetailed(this,
|
||||
else
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_FIND_UNWRAP_METHOD).error(e).callerParam(type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Default method
|
||||
return null;
|
||||
}
|
||||
|
||||
// Players should /always/ be able to be unwrapped
|
||||
// We should only get here if the 'Player' is a proxy
|
||||
private Unwrapper getProxyUnwrapper(final Class<?> type) {
|
||||
try {
|
||||
if (Player.class.isAssignableFrom(type)) {
|
||||
final Method getHandle = MinecraftReflection.getCraftPlayerClass().getMethod("getHandle");
|
||||
|
||||
Unwrapper unwrapper = wrapped -> {
|
||||
try {
|
||||
return getHandle.invoke(((Player) wrapped).getPlayer());
|
||||
} catch (Throwable ex) {
|
||||
try {
|
||||
return getHandle.invoke(Bukkit.getPlayer(((Player) wrapped).getUniqueId()));
|
||||
} catch (ReflectiveOperationException ex1) {
|
||||
throw new RuntimeException("Failed to unwrap proxy " + wrapped, ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
UNWRAPPER_CACHE.put(type, unwrapper);
|
||||
return unwrapper;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(type, "handle", null);
|
||||
if (accessor != null) {
|
||||
if (find != null) {
|
||||
Unwrapper fieldUnwrapper = new Unwrapper() {
|
||||
@Override
|
||||
public Object unwrapItem(Object wrappedObject) {
|
||||
try {
|
||||
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, accessor.getField())
|
||||
if (wrappedObject instanceof Class)
|
||||
return checkClass((Class<?>) wrappedObject, type, find.getType());
|
||||
return FieldUtils.readField(find, wrappedObject, true);
|
||||
} catch (IllegalAccessException e) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_READ_FIELD_HANDLE).error(e).callerParam(wrappedObject, find)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
UNWRAPPER_CACHE.put(type, fieldUnwrapper);
|
||||
|
||||
unwrapperCache.put(type, fieldUnwrapper);
|
||||
return fieldUnwrapper;
|
||||
|
||||
|
||||
} else {
|
||||
// Inform about this too
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_READ_FIELD_HANDLE).callerParam(type)
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_READ_FIELD_HANDLE).callerParam(find)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> checkClass(Class<?> input, Class<?> expected, Class<?> result) {
|
||||
if (expected.isAssignableFrom(input)) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.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.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* A protocol manager that delays all packet listener registrations and unregistrations until
|
||||
* an underlying protocol manager can be constructed.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class DelayedPacketManager implements InternalManager {
|
||||
// Registering packet IDs that are not supported
|
||||
public static final ReportType REPORT_CANNOT_SEND_QUEUED_PACKET = new ReportType("Cannot send queued packet %s.");
|
||||
public static final ReportType REPORT_CANNOT_REGISTER_QUEUED_LISTENER = new ReportType("Cannot register queued listener %s.");
|
||||
|
||||
private volatile InternalManager delegate;
|
||||
|
||||
// Queued actions
|
||||
private final List<Runnable> queuedActions = Collections.synchronizedList(Lists.<Runnable>newArrayList());
|
||||
private final List<PacketListener> queuedListeners = Collections.synchronizedList(Lists.<PacketListener>newArrayList());
|
||||
|
||||
private AsynchronousManager asyncManager;
|
||||
private ErrorReporter reporter;
|
||||
|
||||
// The current hook
|
||||
private PlayerInjectHooks hook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||
|
||||
// If we have been closed
|
||||
private boolean closed;
|
||||
private boolean debug;
|
||||
|
||||
// Queued registration
|
||||
private PluginManager queuedManager;
|
||||
private Plugin queuedPlugin;
|
||||
|
||||
private MinecraftVersion version;
|
||||
|
||||
public DelayedPacketManager(@Nonnull ErrorReporter reporter, @Nonnull MinecraftVersion version) {
|
||||
Preconditions.checkNotNull(reporter, "reporter cannot be NULL.");
|
||||
Preconditions.checkNotNull(version, "version cannot be NULL.");
|
||||
|
||||
this.reporter = reporter;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the underlying protocol manager.
|
||||
* @return The underlying manager.
|
||||
*/
|
||||
public InternalManager getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(Player player) {
|
||||
if (delegate != null)
|
||||
return delegate.getProtocolVersion(player);
|
||||
else
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftVersion getMinecraftVersion() {
|
||||
if (delegate != null)
|
||||
return delegate.getMinecraftVersion();
|
||||
else
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the delegate to the underlying manager.
|
||||
* <p>
|
||||
* This will prompt this packet manager to immediately transmit and
|
||||
* register all queued packets an listeners.
|
||||
* @param delegate - delegate to the new manager.
|
||||
*/
|
||||
protected void setDelegate(InternalManager delegate) {
|
||||
this.delegate = delegate;
|
||||
|
||||
if (delegate != null) {
|
||||
// Update the hook if needed
|
||||
if (!Objects.equal(delegate.getPlayerHook(), hook)) {
|
||||
delegate.setPlayerHook(hook);
|
||||
}
|
||||
// Register events as well
|
||||
if (queuedManager != null && queuedPlugin != null) {
|
||||
delegate.registerEvents(queuedManager, queuedPlugin);
|
||||
}
|
||||
// And update the debug mode
|
||||
delegate.setDebug(debug);
|
||||
|
||||
// Add any pending listeners
|
||||
synchronized (queuedListeners) {
|
||||
for (PacketListener listener : queuedListeners) {
|
||||
try {
|
||||
delegate.addPacketListener(listener);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Inform about this plugin error
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_REGISTER_QUEUED_LISTENER).
|
||||
callerParam(delegate).messageParam(listener).error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute any delayed actions
|
||||
synchronized (queuedActions) {
|
||||
for (Runnable action : queuedActions) {
|
||||
action.run();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't keep this around anymore
|
||||
queuedListeners.clear();
|
||||
queuedActions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable queuedAddPacket(final ConnectionSide side, final Player player, final PacketContainer packet,
|
||||
final NetworkMarker marker, final boolean filtered) {
|
||||
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Attempt to send it now
|
||||
switch (side) {
|
||||
case CLIENT_SIDE:
|
||||
delegate.recieveClientPacket(player, packet, marker, filtered);
|
||||
break;
|
||||
case SERVER_SIDE:
|
||||
delegate.sendServerPacket(player, packet, marker, filtered);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("side cannot be " + side);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Inform about this plugin error
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_SEND_QUEUED_PACKET).
|
||||
callerParam(delegate).messageParam(packet).error(e));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
||||
this.hook = playerHook;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerInjectHooks getPlayerHook() {
|
||||
return hook;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet) throws InvocationTargetException {
|
||||
sendServerPacket(receiver, packet, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters) throws InvocationTargetException {
|
||||
sendServerPacket(receiver, packet, null, filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
|
||||
if (delegate != null) {
|
||||
delegate.sendServerPacket(receiver, packet, marker, filters);
|
||||
} else {
|
||||
queuedActions.add(queuedAddPacket(ConnectionSide.SERVER_SIDE, receiver, packet, marker, filters));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet) throws IllegalAccessException, InvocationTargetException {
|
||||
recieveClientPacket(sender, packet, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters) throws IllegalAccessException, InvocationTargetException {
|
||||
recieveClientPacket(sender, packet, null, filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet, NetworkMarker marker, boolean filters) throws IllegalAccessException, InvocationTargetException {
|
||||
if (delegate != null) {
|
||||
delegate.recieveClientPacket(sender, packet, marker, filters);
|
||||
} else {
|
||||
queuedActions.add(queuedAddPacket(ConnectionSide.CLIENT_SIDE, sender, packet, marker, filters));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastServerPacket(final PacketContainer packet, final Entity entity, final boolean includeTracker) {
|
||||
if (delegate != null) {
|
||||
delegate.broadcastServerPacket(packet, entity, includeTracker);
|
||||
} else {
|
||||
queuedActions.add(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delegate.broadcastServerPacket(packet, entity, includeTracker);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastServerPacket(final PacketContainer packet, final Location origin, final int maxObserverDistance) {
|
||||
if (delegate != null) {
|
||||
delegate.broadcastServerPacket(packet, origin, maxObserverDistance);
|
||||
} else {
|
||||
queuedActions.add(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delegate.broadcastServerPacket(packet, origin, maxObserverDistance);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastServerPacket(final PacketContainer packet) {
|
||||
if (delegate != null) {
|
||||
delegate.broadcastServerPacket(packet);
|
||||
} else {
|
||||
queuedActions.add(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delegate.broadcastServerPacket(packet);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<PacketListener> getPacketListeners() {
|
||||
if (delegate != null)
|
||||
return delegate.getPacketListeners();
|
||||
else
|
||||
return ImmutableSet.copyOf(queuedListeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPacketListener(PacketListener listener) {
|
||||
if (delegate != null)
|
||||
delegate.addPacketListener(listener);
|
||||
else
|
||||
queuedListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePacketListener(PacketListener listener) {
|
||||
if (delegate != null)
|
||||
delegate.removePacketListener(listener);
|
||||
else
|
||||
queuedListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePacketListeners(Plugin plugin) {
|
||||
if (delegate != null) {
|
||||
delegate.removePacketListeners(plugin);
|
||||
} else {
|
||||
for (Iterator<PacketListener> it = queuedListeners.iterator(); it.hasNext(); ) {
|
||||
// Remove listeners of the same plugin
|
||||
if (Objects.equal(it.next().getPlugin(), plugin)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public PacketContainer createPacket(int id) {
|
||||
if (delegate != null)
|
||||
return delegate.createPacket(id);
|
||||
return createPacket(id, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public PacketContainer createPacket(int id, boolean forceDefaults) {
|
||||
if (delegate != null) {
|
||||
return delegate.createPacket(id);
|
||||
} else {
|
||||
// Fallback implementation
|
||||
PacketContainer packet = new PacketContainer(id);
|
||||
|
||||
// Use any default values if possible
|
||||
if (forceDefaults) {
|
||||
try {
|
||||
packet.getModifier().writeDefaults();
|
||||
} catch (FieldAccessException e) {
|
||||
throw new RuntimeException("Security exception.", e);
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public PacketConstructor createPacketConstructor(int id, Object... arguments) {
|
||||
if (delegate != null)
|
||||
return delegate.createPacketConstructor(id, arguments);
|
||||
else
|
||||
return PacketConstructor.DEFAULT.withPacket(id, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketConstructor createPacketConstructor(PacketType type, Object... arguments) {
|
||||
if (delegate != null)
|
||||
return delegate.createPacketConstructor(type, arguments);
|
||||
else
|
||||
return PacketConstructor.DEFAULT.withPacket(type, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Set<Integer> getSendingFilters() {
|
||||
if (delegate != null) {
|
||||
return delegate.getSendingFilters();
|
||||
} else {
|
||||
// Linear scan is fast enough here
|
||||
Set<Integer> sending = Sets.newHashSet();
|
||||
|
||||
for (PacketListener listener : queuedListeners) {
|
||||
sending.addAll(listener.getSendingWhitelist().getWhitelist());
|
||||
}
|
||||
return sending;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Set<Integer> getReceivingFilters() {
|
||||
if (delegate != null) {
|
||||
return delegate.getReceivingFilters();
|
||||
} else {
|
||||
Set<Integer> recieving = Sets.newHashSet();
|
||||
|
||||
for (PacketListener listener : queuedListeners) {
|
||||
recieving.addAll(listener.getReceivingWhitelist().getWhitelist());
|
||||
}
|
||||
return recieving;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketContainer createPacket(PacketType type) {
|
||||
return createPacket(type.getLegacyId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketContainer createPacket(PacketType type, boolean forceDefaults) {
|
||||
return createPacket(type.getLegacyId(), forceDefaults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getSendingFilterTypes() {
|
||||
return PacketRegistry.toPacketTypes(getSendingFilters(), Sender.SERVER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getReceivingFilterTypes() {
|
||||
return PacketRegistry.toPacketTypes(getReceivingFilters(), Sender.CLIENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
|
||||
if (delegate != null)
|
||||
delegate.updateEntity(entity, observers);
|
||||
else
|
||||
EntityUtilities.updateEntity(entity, observers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getEntityFromID(World container, int id) throws FieldAccessException {
|
||||
if (delegate != null)
|
||||
return delegate.getEntityFromID(container, id);
|
||||
else
|
||||
return EntityUtilities.getEntityFromID(container, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Player> getEntityTrackers(Entity entity) throws FieldAccessException {
|
||||
if (delegate != null)
|
||||
return delegate.getEntityTrackers(entity);
|
||||
else
|
||||
return EntityUtilities.getEntityTrackers(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closed || (delegate != null && delegate.isClosed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsynchronousManager getAsynchronousManager() {
|
||||
if (delegate != null)
|
||||
return delegate.getAsynchronousManager();
|
||||
else
|
||||
return asyncManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
|
||||
if (delegate != null) {
|
||||
delegate.setDebug(debug);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the asynchronous manager. This must be set.
|
||||
* @param asyncManager - the asynchronous manager.
|
||||
*/
|
||||
public void setAsynchronousManager(AsynchronousManager asyncManager) {
|
||||
this.asyncManager = asyncManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerEvents(PluginManager manager, Plugin plugin) {
|
||||
if (delegate != null) {
|
||||
delegate.registerEvents(manager, plugin);
|
||||
} else {
|
||||
queuedManager = manager;
|
||||
queuedPlugin = plugin;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (delegate != null)
|
||||
delegate.close();
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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 java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.WrappedIntHashMap;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* Used to perform certain operations on entities.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class EntityUtilities {
|
||||
|
||||
private static Field entityTrackerField;
|
||||
private static Field trackedEntitiesField;
|
||||
private static Field trackedPlayersField;
|
||||
private static Field trackerField;
|
||||
|
||||
private static Method scanPlayersMethod;
|
||||
|
||||
// Fix: PaperSpigot changed the trackedPlayers field
|
||||
private static Collection<?> getCollection(Object value) {
|
||||
if (value instanceof Collection<?>) {
|
||||
return (Collection<?>) value;
|
||||
} else if (value instanceof Map<?, ?>) {
|
||||
return ((Map<?, ?>) value).keySet();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Expected Collection or Map but got " + value.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* While this function may look pretty bad, it's essentially just a reflection-warped
|
||||
* version of the following:
|
||||
*
|
||||
* @SuppressWarnings("unchecked")
|
||||
* public static void updateEntity2(Entity entity, List<Player> observers) {
|
||||
*
|
||||
* World world = entity.getWorld();
|
||||
* WorldServer worldServer = ((CraftWorld) world).getHandle();
|
||||
*
|
||||
* EntityTracker tracker = worldServer.tracker;
|
||||
* EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(entity.getEntityId());
|
||||
*
|
||||
* List<EntityPlayer> nmsPlayers = getNmsPlayers(observers);
|
||||
*
|
||||
* entry.trackedPlayers.removeAll(nmsPlayers);
|
||||
* entry.scanPlayers(nmsPlayers);
|
||||
* }
|
||||
*
|
||||
* private static List<EntityPlayer> getNmsPlayers(List<Player> players) {
|
||||
* List<EntityPlayer> nsmPlayers = new ArrayList<EntityPlayer>();
|
||||
*
|
||||
* for (Player bukkitPlayer : players) {
|
||||
* CraftPlayer craftPlayer = (CraftPlayer) bukkitPlayer;
|
||||
* nsmPlayers.add(craftPlayer.getHandle());
|
||||
* }
|
||||
*
|
||||
* return nsmPlayers;
|
||||
* }
|
||||
*
|
||||
*/
|
||||
public static void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
|
||||
try {
|
||||
// Fix: Throw a more informative error
|
||||
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
|
||||
if (trackerEntry == null) {
|
||||
throw new IllegalArgumentException("Cannot find entity trackers for " + entity + (entity.isDead() ? " - entity is dead." : "."));
|
||||
}
|
||||
|
||||
if (trackedPlayersField == null) {
|
||||
// This one is fairly easy
|
||||
trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*");
|
||||
}
|
||||
|
||||
// Phew, finally there.
|
||||
Collection<?> trackedPlayers = getCollection(FieldUtils.readField(trackedPlayersField, trackerEntry, false));
|
||||
List<Object> nmsPlayers = unwrapBukkit(observers);
|
||||
|
||||
trackedPlayers.removeAll(nmsPlayers);
|
||||
|
||||
// We have to rely on a NAME once again. Damn it.
|
||||
if (scanPlayersMethod == null) {
|
||||
scanPlayersMethod = trackerEntry.getClass().getMethod("scanPlayers", List.class);
|
||||
}
|
||||
|
||||
scanPlayersMethod.invoke(trackerEntry, nmsPlayers);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Security limitation prevents access to 'get' method in IntHashMap", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Exception occurred in Minecraft.", e);
|
||||
} catch (SecurityException e) {
|
||||
throw new FieldAccessException("Security limitation prevents access to 'scanPlayers' method in trackerEntry.", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new FieldAccessException("Cannot find 'scanPlayers' method. Is ProtocolLib up to date?", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static List<Player> getEntityTrackers(Entity entity) {
|
||||
try {
|
||||
List<Player> result = new ArrayList<Player>();
|
||||
|
||||
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
|
||||
if (trackerEntry == null) {
|
||||
throw new IllegalArgumentException("Cannot find entity trackers for " + entity + (entity.isDead() ? " - entity is dead." : "."));
|
||||
}
|
||||
|
||||
if (trackedPlayersField == null) {
|
||||
trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*");
|
||||
}
|
||||
|
||||
Collection<?> trackedPlayers = getCollection(FieldUtils.readField(trackedPlayersField, trackerEntry, false));
|
||||
|
||||
// Wrap every player - we also ensure that the underlying tracker list is immutable
|
||||
for (Object tracker : trackedPlayers) {
|
||||
if (MinecraftReflection.isMinecraftPlayer(tracker)) {
|
||||
result.add((Player) MinecraftReflection.getBukkitEntity(tracker));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Security limitation prevented access to the list of tracked players.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new FieldAccessException("Exception occurred in Minecraft.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the entity tracker entry given a ID.
|
||||
* @param world - world server.
|
||||
* @param entityID - entity ID.
|
||||
* @return The entity tracker entry.
|
||||
* @throws FieldAccessException
|
||||
*/
|
||||
private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
|
||||
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
|
||||
Object worldServer = unwrapper.unwrapItem(world);
|
||||
|
||||
if (entityTrackerField == null)
|
||||
entityTrackerField = FuzzyReflection.fromObject(worldServer).
|
||||
getFieldByType("tracker", MinecraftReflection.getEntityTrackerClass());
|
||||
|
||||
// Get the tracker
|
||||
Object tracker = null;
|
||||
|
||||
try {
|
||||
tracker = FieldUtils.readField(entityTrackerField, worldServer, false);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot access 'tracker' field due to security limitations.", e);
|
||||
}
|
||||
|
||||
if (trackedEntitiesField == null) {
|
||||
@SuppressWarnings("rawtypes")
|
||||
Set<Class> ignoredTypes = new HashSet<Class>();
|
||||
|
||||
// Well, this is more difficult. But we're looking for a Minecraft object that is not
|
||||
// created by the constructor(s).
|
||||
for (Constructor<?> constructor : tracker.getClass().getConstructors()) {
|
||||
for (Class<?> type : constructor.getParameterTypes()) {
|
||||
ignoredTypes.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
// The Minecraft field that's NOT filled in by the constructor
|
||||
trackedEntitiesField = FuzzyReflection.fromObject(tracker, true).
|
||||
getFieldByType(MinecraftReflection.getMinecraftObjectRegex(), ignoredTypes);
|
||||
}
|
||||
|
||||
// Read the entity hashmap
|
||||
Object trackedEntities = null;
|
||||
|
||||
try {
|
||||
trackedEntities = FieldUtils.readField(trackedEntitiesField, tracker, true);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot access 'trackedEntities' field due to security limitations.", e);
|
||||
}
|
||||
|
||||
return WrappedIntHashMap.fromHandle(trackedEntities).get(entityID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve entity from a ID, even it it's newly created.
|
||||
* @return The asssociated entity.
|
||||
* @throws FieldAccessException Reflection error.
|
||||
*/
|
||||
public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException {
|
||||
try {
|
||||
Object trackerEntry = getEntityTrackerEntry(world, entityID);
|
||||
Object tracker = null;
|
||||
|
||||
// Handle NULL cases
|
||||
if (trackerEntry != null) {
|
||||
if (trackerField == null) {
|
||||
try {
|
||||
trackerField = trackerEntry.getClass().getField("tracker");
|
||||
} catch (NoSuchFieldException e) {
|
||||
// Assume it's the first public entity field then
|
||||
trackerField = FuzzyReflection.fromObject(trackerEntry).getFieldByType(
|
||||
"tracker", MinecraftReflection.getEntityClass());
|
||||
}
|
||||
}
|
||||
|
||||
tracker = FieldUtils.readField(trackerField, trackerEntry, true);
|
||||
}
|
||||
|
||||
// If the tracker is NULL, we'll just assume this entity doesn't exist
|
||||
if (tracker != null)
|
||||
return (Entity) MinecraftReflection.getBukkitEntity(tracker);
|
||||
else
|
||||
return null;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new FieldAccessException("Cannot find entity from ID " + entityID + ".", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Object> unwrapBukkit(List<Player> players) {
|
||||
|
||||
List<Object> output = Lists.newArrayList();
|
||||
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
|
||||
|
||||
// Get the NMS equivalent
|
||||
for (Player player : players) {
|
||||
Object result = unwrapper.unwrapItem(player);
|
||||
|
||||
if (result != null)
|
||||
output.add(result);
|
||||
else
|
||||
throw new IllegalArgumentException("Cannot unwrap item " + player);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
+11
-13
@@ -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,29 +27,27 @@ 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() {
|
||||
+22
-12
@@ -1,40 +1,50 @@
|
||||
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;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void registerEvents(PluginManager manager, final Plugin plugin);
|
||||
|
||||
public void registerEvents(PluginManager manager, final Plugin plugin);
|
||||
|
||||
/**
|
||||
* Called when ProtocolLib is closing.
|
||||
*/
|
||||
void close();
|
||||
public void close();
|
||||
|
||||
/**
|
||||
* Determine if debug mode is enabled.
|
||||
*
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
boolean isDebug();
|
||||
|
||||
public boolean isDebug();
|
||||
|
||||
/**
|
||||
* Set whether or not debug mode is enabled.
|
||||
*
|
||||
* @param debug - TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
void setDebug(boolean debug);
|
||||
public void setDebug(boolean debug);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public abstract void invokePacketRecieving(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener.
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
public abstract void invokePacketSending(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Retrieve the associated ID of a packet.
|
||||
* @param packet - the packet.
|
||||
* @return The packet ID.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract int getPacketID(Object packet);
|
||||
|
||||
/**
|
||||
* Retrieve the associated type of a packet.
|
||||
* @param packet - the packet.
|
||||
* @return The packet type.
|
||||
*/
|
||||
public abstract PacketType getPacketType(Object packet);
|
||||
|
||||
/**
|
||||
* Retrieve the object responsible for intercepting write packets.
|
||||
* @return Object that intercepts write packets.
|
||||
*/
|
||||
public InterceptWritePacket getInterceptWritePacket();
|
||||
|
||||
/**
|
||||
* Determine if a given packet requires input buffering.
|
||||
* @param packetId - the packet to check.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean requireInputBuffer(int packetId);
|
||||
|
||||
/**
|
||||
* Associate a given class with the given packet ID. Internal method.
|
||||
* @param clazz - class to associate.
|
||||
*/
|
||||
public abstract void unregisterPacketClass(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Register a given class in the packet registry. Internal method.
|
||||
* @param clazz - class to register.
|
||||
* @param packetID - the the new associated packet ID.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract void registerPacketClass(Class<?> clazz, int packetID);
|
||||
|
||||
/**
|
||||
* Retrieves the correct packet class from a given packet ID.
|
||||
* @param packetID - the packet ID.
|
||||
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
||||
* @return The associated class.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract Class<?> getPacketClassFromID(int packetID, boolean forceVanilla);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
/**
|
||||
* Packets that are known to be transmitted during login.
|
||||
* <p>
|
||||
* This may be dynamically extended later.
|
||||
* @author Kristian
|
||||
*/
|
||||
class LoginPackets {
|
||||
private IntegerSet clientSide = new IntegerSet(Packets.PACKET_COUNT);
|
||||
private IntegerSet serverSide = new IntegerSet(Packets.PACKET_COUNT);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public LoginPackets(MinecraftVersion version) {
|
||||
// Ordinary login
|
||||
clientSide.add(Packets.Client.HANDSHAKE);
|
||||
serverSide.add(Packets.Server.KEY_REQUEST);
|
||||
clientSide.add(Packets.Client.KEY_RESPONSE);
|
||||
serverSide.add(Packets.Server.KEY_RESPONSE);
|
||||
clientSide.add(Packets.Client.CLIENT_COMMAND);
|
||||
serverSide.add(Packets.Server.LOGIN);
|
||||
|
||||
// List ping
|
||||
clientSide.add(Packets.Client.GET_INFO);
|
||||
|
||||
// In 1.6.2, Minecraft started sending CUSTOM_PAYLOAD in the server list protocol
|
||||
if (version.compareTo(MinecraftVersion.HORSE_UPDATE) >= 0 || triesForgeIntegration()) {
|
||||
clientSide.add(Packets.Client.CUSTOM_PAYLOAD);
|
||||
}
|
||||
serverSide.add(Packets.Server.KICK_DISCONNECT);
|
||||
|
||||
// Forge uses packet 250 during login
|
||||
if (triesForgeIntegration()) {
|
||||
serverSide.add(Packets.Client.CUSTOM_PAYLOAD);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we are runnign MCPC.
|
||||
* @return TRUE if we are, FALSE otherwise.
|
||||
*/
|
||||
private static boolean triesForgeIntegration() {
|
||||
String version = Bukkit.getVersion();
|
||||
return version.contains("MCPC-Plus") || version.contains("Cauldron") || version.contains("Thermos");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a packet may be sent during login from a given direction.
|
||||
* @param packetId - the ID of the packet.
|
||||
* @param side - the direction.
|
||||
* @return TRUE if it may, FALSE otherwise.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isLoginPacket(int packetId, ConnectionSide side) {
|
||||
switch (side) {
|
||||
case CLIENT_SIDE:
|
||||
return clientSide.contains(packetId);
|
||||
case SERVER_SIDE:
|
||||
return serverSide.contains(packetId);
|
||||
case BOTH:
|
||||
return clientSide.contains(packetId) ||
|
||||
serverSide.contains(packetId);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown connection side: " + side);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given packet may be sent during login.
|
||||
* @param type - the packet type.
|
||||
* @return TRUE if it may, FALSE otherwise.
|
||||
*/
|
||||
public boolean isLoginPacket(PacketType type) {
|
||||
if (!MinecraftReflection.isUsingNetty())
|
||||
return isLoginPacket(type.getLegacyId(), type.getSender().toSide());
|
||||
|
||||
return PacketType.Login.Client.getInstance().hasMember(type) ||
|
||||
PacketType.Login.Server.getInstance().hasMember(type) ||
|
||||
PacketType.Status.Client.getInstance().hasMember(type) ||
|
||||
PacketType.Status.Server.getInstance().hasMember(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Represents a processor for network markers.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class NetworkProcessor {
|
||||
private 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 marker - the network marker, or NULL.
|
||||
*/
|
||||
public void invokePostEvent(PacketEvent event, NetworkMarker marker) {
|
||||
if (marker == null)
|
||||
return;
|
||||
|
||||
if (NetworkMarker.hasPostListeners(marker)) {
|
||||
// Invoke every sent listener
|
||||
for (PacketPostListener listener : marker.getPostListeners()) {
|
||||
try {
|
||||
listener.onPostEvent(event);
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
reporter.reportMinimal(listener.getPlugin(), "SentListener.run()", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
if (scheduled != null) {
|
||||
for (ScheduledPacket packet : scheduled) {
|
||||
packet.schedule(manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+106
-96
@@ -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.injector;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.error.RethrowErrorReporter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
@@ -26,117 +30,70 @@ 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
|
||||
*
|
||||
*/
|
||||
public class PacketConstructor {
|
||||
|
||||
/**
|
||||
* A packet constructor that automatically converts Bukkit types to their NMS conterpart.
|
||||
* 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 final Constructor<?> constructorMethod;
|
||||
// Used to unwrap Bukkit objects
|
||||
private final List<Unwrapper> unwrappers;
|
||||
|
||||
private Constructor<?> constructorMethod;
|
||||
|
||||
// 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.addAll(BukkitConverters.getUnwrappers());
|
||||
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();
|
||||
return type.getLegacyId();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -144,12 +101,27 @@ public class PacketConstructor {
|
||||
return new PacketConstructor(type, constructorMethod, unwrappers, paramUnwrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a packet constructor that creates packets using the given ID.
|
||||
* <p>
|
||||
* Note that if you pass a Class<?> as a value, it will use its type directly.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #withPacket(PacketType, Object[])} instead.
|
||||
* @param id - legacy (1.6.4) packet ID.
|
||||
* @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.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketConstructor withPacket(int id, Object[] values) {
|
||||
return withPacket(PacketType.findLegacy(id), values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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 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.
|
||||
@@ -157,16 +129,16 @@ public class PacketConstructor {
|
||||
public PacketConstructor withPacket(PacketType type, Object[] values) {
|
||||
Class<?>[] types = new Class<?>[values.length];
|
||||
Throwable lastException = null;
|
||||
Unwrapper[] paramUnwrapper = new Unwrapper[values.length];
|
||||
|
||||
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) {
|
||||
@@ -176,7 +148,7 @@ public class PacketConstructor {
|
||||
} catch (Throwable e) {
|
||||
lastException = e;
|
||||
}
|
||||
|
||||
|
||||
// Update type we're searching for
|
||||
if (result != null) {
|
||||
types[i] = PacketConstructor.getClass(result);
|
||||
@@ -184,17 +156,19 @@ public class PacketConstructor {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// Try it
|
||||
types[i] = Object.class;
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> packetClass = PacketRegistry.getPacketClassFromType(type);
|
||||
|
||||
Class<?> packetType = PacketRegistry.getPacketClassFromType(type, true);
|
||||
|
||||
if (packetType == null)
|
||||
throw new IllegalArgumentException("Could not find a packet by the type " + type);
|
||||
|
||||
// Find the correct constructor
|
||||
for (Constructor<?> constructor : packetClass.getConstructors()) {
|
||||
for (Constructor<?> constructor : packetType.getConstructors()) {
|
||||
Class<?>[] params = constructor.getParameterTypes();
|
||||
|
||||
if (isCompatible(types, params)) {
|
||||
@@ -204,15 +178,14 @@ 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 {
|
||||
@@ -222,10 +195,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) {
|
||||
@@ -236,20 +209,57 @@ 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.
|
||||
*/
|
||||
@@ -0,0 +1,256 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.event.world.WorldInitEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.executors.BukkitFutures;
|
||||
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.player.InjectedServerConnection;
|
||||
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
|
||||
public class PacketFilterBuilder {
|
||||
public static final ReportType REPORT_TEMPORARY_EVENT_ERROR = new ReportType("Unable to register or handle temporary event.");
|
||||
public static final ReportType REPORT_SPIGOT_IS_DELAYING_INJECTOR = new ReportType("Delaying due to Spigot.");
|
||||
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a reference to the current ProtocolLib instance.
|
||||
* @return ProtocolLib.
|
||||
*/
|
||||
public Plugin getLibrary() {
|
||||
return 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the error reporter.
|
||||
* @return Error reporter.
|
||||
*/
|
||||
public ErrorReporter getReporter() {
|
||||
return reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the asynchronous manager.
|
||||
* <p>
|
||||
* This is first constructed the {@link #build()} method.
|
||||
* @return The asynchronous manager.
|
||||
*/
|
||||
public AsyncFilterManager getAsyncManager() {
|
||||
return asyncManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new packet filter manager.
|
||||
* @return A new packet filter manager.
|
||||
*/
|
||||
public InternalManager build() {
|
||||
if (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;
|
||||
|
||||
// Spigot
|
||||
if (SpigotPacketInjector.canUseSpigotListener()) {
|
||||
// If the server hasn't loaded yet - wait
|
||||
if (InjectedServerConnection.getServerConnection(reporter, server) == null) {
|
||||
// We need to delay this until we know if Netty is enabled
|
||||
final DelayedPacketManager delayed = new DelayedPacketManager(reporter, mcVersion);
|
||||
|
||||
// They must reference each other
|
||||
delayed.setAsynchronousManager(asyncManager);
|
||||
asyncManager.setManager(delayed);
|
||||
|
||||
Futures.addCallback(BukkitFutures.nextEvent(library, WorldInitEvent.class),
|
||||
new FutureCallback<WorldInitEvent>() {
|
||||
@Override
|
||||
public void onSuccess(WorldInitEvent event) {
|
||||
// Nevermind
|
||||
if (delayed.isClosed())
|
||||
return;
|
||||
|
||||
try {
|
||||
registerSpigot(delayed);
|
||||
} catch (Exception e) {
|
||||
onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
reporter.reportWarning(PacketFilterBuilder.this, Report
|
||||
.newBuilder(REPORT_TEMPORARY_EVENT_ERROR).error(error));
|
||||
}
|
||||
});
|
||||
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_SPIGOT_IS_DELAYING_INJECTOR));
|
||||
|
||||
// Let plugins use this version instead
|
||||
return delayed;
|
||||
} else {
|
||||
nettyEnabled = !MinecraftReflection.isMinecraftObject(
|
||||
InjectedServerConnection.getServerConnection(reporter, server));
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise - construct the packet filter manager right away
|
||||
return buildInternal();
|
||||
}
|
||||
|
||||
private void registerSpigot(DelayedPacketManager delayed) {
|
||||
// Use netty if we have a non-standard ServerConnection class
|
||||
nettyEnabled = !MinecraftReflection.isMinecraftObject(
|
||||
InjectedServerConnection.getServerConnection(reporter, server));
|
||||
|
||||
// Switch to the standard manager
|
||||
delayed.setDelegate(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;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
+12
-20
@@ -4,9 +4,7 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
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;
|
||||
|
||||
@@ -56,12 +54,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 = ImmutableSet.of("mcore", "MassiveCore");
|
||||
private static final Set<String> DYNAMIC_DEPENDENCY = Sets.newHashSet("mcore", "MassiveCore");
|
||||
|
||||
/**
|
||||
* Set of plugins that have been loaded after ProtocolLib.
|
||||
*/
|
||||
private final Set<String> loadedAfter = new HashSet<>();
|
||||
private final Set<String> loadedAfter = new HashSet<String>();
|
||||
|
||||
/**
|
||||
* Reference to ProtocolLib.
|
||||
@@ -75,16 +73,10 @@ class PluginVerifier {
|
||||
public PluginVerifier(Plugin dependency) {
|
||||
if (dependency == null)
|
||||
throw new IllegalArgumentException("dependency cannot be NULL.");
|
||||
|
||||
try {
|
||||
// This would screw with the assumption in hasDependency(Plugin, Plugin)
|
||||
if (safeConversion(dependency.getDescription().getLoadBefore()).size() > 0)
|
||||
throw new IllegalArgumentException("dependency cannot have a load directives.");
|
||||
} catch (LinkageError e) {
|
||||
// They're probably using an ancient version of Bukkit
|
||||
dependency.getLogger().log(Level.WARNING, "Failed to determine loadBefore: " + e);
|
||||
}
|
||||
|
||||
// This would screw with the assumption in hasDependency(Plugin, Plugin)
|
||||
if (safeConversion(dependency.getDescription().getLoadBefore()).size() > 0)
|
||||
throw new IllegalArgumentException("dependency cannot have a load directives.");
|
||||
|
||||
this.dependency = dependency;
|
||||
}
|
||||
|
||||
@@ -101,7 +93,7 @@ class PluginVerifier {
|
||||
if (plugin != null)
|
||||
return plugin;
|
||||
else
|
||||
throw new PluginNotFoundException("Cannot find plugin " + pluginName);
|
||||
throw new PluginNotFoundException("Cannot find plugin " + pluginName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +153,7 @@ class PluginVerifier {
|
||||
/**
|
||||
* Determine if a given plugin is guarenteed to be loaded before the other.
|
||||
* <p>
|
||||
* Note that the before plugin is assumed to have no "load" directives - that is, plugins to be
|
||||
* Note that the before plugin is assumed to have no "load" directives - that is, plugins to be
|
||||
* loaded after itself. The after plugin may have "load" directives, but it is irrelevant for our purposes.
|
||||
* @param beforePlugin - the plugin that is loaded first.
|
||||
* @param afterPlugin - the plugin that is loaded last.
|
||||
@@ -174,7 +166,7 @@ class PluginVerifier {
|
||||
}
|
||||
|
||||
// No dependency - check the load order
|
||||
if (beforePlugin.getDescription().getLoad() == PluginLoadOrder.STARTUP &&
|
||||
if (beforePlugin.getDescription().getLoad() == PluginLoadOrder.STARTUP &&
|
||||
afterPlugin.getDescription().getLoad() == PluginLoadOrder.POSTWORLD) {
|
||||
return true;
|
||||
}
|
||||
@@ -185,14 +177,14 @@ class PluginVerifier {
|
||||
* Determine if a plugin has a given dependency, either directly or indirectly.
|
||||
* @param plugin - the plugin to check.
|
||||
* @param dependency - the dependency to find.
|
||||
* @return TRUE if the plugin has the given dependency, FALSE otherwise.
|
||||
* @return TRUE if the plugin has the given dependency, FALSE otherwise.
|
||||
*/
|
||||
private boolean hasDependency(Plugin plugin, Plugin dependency) {
|
||||
return hasDependency(plugin, dependency, Sets.<String>newHashSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a list to a set.
|
||||
* Convert a list to a set.
|
||||
* <p>
|
||||
* A null list will be converted to an empty set.
|
||||
* @param list - the list to convert.
|
||||
@@ -202,7 +194,7 @@ class PluginVerifier {
|
||||
if (list == null)
|
||||
return Collections.emptySet();
|
||||
else
|
||||
return new HashSet<>(list);
|
||||
return Sets.newHashSet(list);
|
||||
}
|
||||
|
||||
// Avoid cycles. DFS.
|
||||
+1
-1
@@ -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 {
|
||||
+40
-62
@@ -17,25 +17,17 @@
|
||||
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager.ListenerType;
|
||||
import com.comphenix.protocol.timing.TimedTracker;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Registry of synchronous packet listeners.
|
||||
*
|
||||
@@ -116,12 +108,14 @@ public final class SortedPacketListenerList extends AbstractConcurrentListenerMu
|
||||
* @param event - the related packet event.
|
||||
* @param element - the listener to invoke.
|
||||
*/
|
||||
private void invokeReceivingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
|
||||
private final void invokeReceivingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
|
||||
try {
|
||||
event.setReadOnly(element.getPriority() == ListenerPriority.MONITOR);
|
||||
element.getListener().onPacketReceiving(event);
|
||||
|
||||
} catch (OutOfMemoryError | ThreadDeath e) {
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Minecraft doesn't want your Exception.
|
||||
@@ -136,89 +130,73 @@ public final class SortedPacketListenerList extends AbstractConcurrentListenerMu
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
public void invokePacketSending(ErrorReporter reporter, PacketEvent event) {
|
||||
invokePacketSending(reporter, event, null);
|
||||
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketType());
|
||||
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
if (timedManager.isTiming()) {
|
||||
for (PrioritizedListener<PacketListener> element : list) {
|
||||
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_SERVER_SIDE);
|
||||
long token = tracker.beginTracking();
|
||||
|
||||
// Measure and record the execution time
|
||||
invokeSendingListener(reporter, event, element);
|
||||
tracker.endTracking(token, event.getPacketType());
|
||||
}
|
||||
} else {
|
||||
for (PrioritizedListener<PacketListener> element : list) {
|
||||
invokeSendingListener(reporter, event, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener of the given priority.
|
||||
* @param reporter - the error reporter that will be used to inform about listener exceptions.
|
||||
* @param event - the packet event to invoke.
|
||||
* @param priorityFilter - the priority for a listener to be invoked. If null is provided, every registered listener will be invoked
|
||||
* @param priorityFilter - the required priority for a listener to be invoked.
|
||||
*/
|
||||
public void invokePacketSending(ErrorReporter reporter, PacketEvent event, @Nullable ListenerPriority priorityFilter) {
|
||||
invokeUnpackedPacketSending(reporter, event, priorityFilter);
|
||||
if (event.getPacketType() == PacketType.Play.Server.BUNDLE && !event.isCancelled()) {
|
||||
// unpack the bundle and invoke for each packet in the bundle
|
||||
Iterable<PacketContainer> packets = event.getPacket().getPacketBundles().read(0);
|
||||
List<PacketContainer> outPackets = new ArrayList<>();
|
||||
for (PacketContainer subPacket : packets) {
|
||||
if(subPacket == null) {
|
||||
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "Failed to invoke packet event " + (priorityFilter == null ? "" : ("with priority " + priorityFilter)) + " in bundle because bundle contains null packet: " + packets, new Throwable());
|
||||
continue;
|
||||
}
|
||||
PacketEvent subPacketEvent = PacketEvent.fromServer(this, subPacket, event.getNetworkMarker(), event.getPlayer());
|
||||
invokeUnpackedPacketSending(reporter, subPacketEvent, priorityFilter);
|
||||
|
||||
if (!subPacketEvent.isCancelled()) {
|
||||
// if the packet event has been cancelled, the packet will be removed from the bundle
|
||||
PacketContainer packet = subPacketEvent.getPacket();
|
||||
if(packet == null) {
|
||||
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "null packet container returned for " + subPacketEvent, new Throwable());
|
||||
} else if(packet.getHandle() == null) {
|
||||
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "null packet handle returned for " + subPacketEvent, new Throwable());
|
||||
} else {
|
||||
outPackets.add(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (packets.iterator().hasNext()) {
|
||||
event.getPacket().getPacketBundles().write(0, outPackets);
|
||||
} else {
|
||||
// cancel entire packet if each individual packet has been cancelled
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void invokeUnpackedPacketSending(ErrorReporter reporter, PacketEvent event, @org.jetbrains.annotations.Nullable ListenerPriority priorityFilter) {
|
||||
public void invokePacketSending(ErrorReporter reporter, PacketEvent event, ListenerPriority priorityFilter) {
|
||||
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketType());
|
||||
|
||||
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
|
||||
if (timedManager.isTiming()) {
|
||||
for (PrioritizedListener<PacketListener> element : list) {
|
||||
if (priorityFilter == null || element.getPriority() == priorityFilter) {
|
||||
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_SERVER_SIDE);
|
||||
long token = tracker.beginTracking();
|
||||
|
||||
// Measure and record the execution time
|
||||
invokeSendingListener(reporter, event, element);
|
||||
tracker.endTracking(token, event.getPacketType());
|
||||
if (element.getPriority() == priorityFilter) {
|
||||
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_SERVER_SIDE);
|
||||
long token = tracker.beginTracking();
|
||||
|
||||
// Measure and record the execution time
|
||||
invokeSendingListener(reporter, event, element);
|
||||
tracker.endTracking(token, event.getPacketType());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (PrioritizedListener<PacketListener> element : list) {
|
||||
if (priorityFilter == null || element.getPriority() == priorityFilter) {
|
||||
if (element.getPriority() == priorityFilter) {
|
||||
invokeSendingListener(reporter, event, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoke a particular sending listener.
|
||||
* @param reporter - the error reporter.
|
||||
* @param event - the related packet event.
|
||||
* @param element - the listener to invoke.
|
||||
*/
|
||||
private void invokeSendingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
|
||||
private final void invokeSendingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
|
||||
try {
|
||||
event.setReadOnly(element.getPriority() == ListenerPriority.MONITOR);
|
||||
element.getListener().onPacketSending(event);
|
||||
|
||||
} catch (OutOfMemoryError | ThreadDeath e) {
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Minecraft doesn't want your Exception.
|
||||
Loaded 100 of 474 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user