Compare commits

..
12 Commits
363 changed files with 14072 additions and 21039 deletions

No files matched your search

-38
View File
@@ -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 [freenode.net](http://freenode.net)
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!
-11
View File
@@ -1,11 +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 an issue caused by another plugin
- [ ] You've checked for duplicate issues
- [ ] You didn't use `/reload`
Debug paste link:
Description and relevant errors:
+8 -21
View File
@@ -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,14 +155,11 @@ pip-log.txt
.coverage
.tox
# Translations
#Translations
*.mo
# Mr Developer
#Mr Developer
.mr.developer.cfg
# Mac crap
.DS_Store
# Log4J files
logs/
-12
View File
@@ -1,12 +0,0 @@
language: java
jdk:
- oraclejdk8
script: mvn clean test
#before_install: cd ProtocolLib
install: true
notifications:
email:
recipients:
- "dmulloy2@live.com"
on_success: change
on_failure: always
+165
View File
@@ -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
+72
View File
@@ -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>
@@ -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>
+165
View File
@@ -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>http://repo.dmulloy2.net/content/groups/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>
@@ -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();
}
}
@@ -1,14 +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.util.Collections;
import java.util.List;
import java.util.Map;
@@ -17,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;
@@ -26,212 +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);
// Used in order to lookup a channel
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
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");
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.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 = 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;
// 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);
@@ -239,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;
}
@@ -268,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.
@@ -277,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) {
@@ -423,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);
+165
View File
@@ -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
@@ -1,29 +1,42 @@
<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>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>4.4.0</version>
<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>
<parent>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>4.4.0-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number>
<project.fullVersion>${project.version}</project.fullVersion>
<powermock.version>1.7.4</powermock.version>
<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>
@@ -42,7 +55,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<version>2.0</version>
<executions>
<execution>
<phase>package</phase>
@@ -65,7 +78,6 @@
<exclude>org.spigotmc:spigot</exclude>
<exclude>org.spigotmc:spigot-api</exclude>
<exclude>junit:junit</exclude>
<exclude>io.netty:*</exclude>
</excludes>
</artifactSet>
</configuration>
@@ -75,60 +87,43 @@
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<version>2.3.2</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>3.1.0</version>
<version>2.1</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>com.comphenix.protocol.Application</Main-Class>
<Implementation-Title>ProtocolLib</Implementation-Title>
<Implementation-Version>${project.fullVersion}</Implementation-Version>
<Implementation-Vendor>dmulloy2</Implementation-Vendor>
</manifestEntries>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
<finalName>${project.name}</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<systemProperties>
<property>
<name>projectVersion</name>
<value>${project.version}</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jenkins</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<project.build.number>-b${env.BUILD_NUMBER}</project.build.number>
<project.fullVersion>${project.version}${project.build.number}</project.fullVersion>
</properties>
</profile>
<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>
@@ -137,15 +132,12 @@
<value>true</value>
</property>
</activation>
<properties>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<version>2.2</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -159,7 +151,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<version>2.9</version>
<executions>
<execution>
<id>attach-javadocs</id>
@@ -170,11 +162,10 @@
</executions>
</plugin>
<!-- TODO: Look into signing releases
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<version>1.4</version>
<executions>
<execution>
<id>sign-artifacts</id>
@@ -185,7 +176,6 @@
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
@@ -193,9 +183,9 @@
</profiles>
<scm>
<connection>scm:git:git://github.com/dmulloy2/ProtocolLib.git</connection>
<developerConnection>scm:git:git@github.com:dmulloy2/ProtocolLib.git</developerConnection>
<url>https://github.com/dmulloy2/ProtocolLib</url>
<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>
@@ -210,7 +200,7 @@
<developer>
<id>dmulloy2</id>
<name>Dan Mulloy</name>
<url>http://dmulloy2.net/</url>
<url>http://shadowvolt.com/</url>
<roles>
<role>developer</role>
<role>maintainer</role>
@@ -230,23 +220,37 @@
<dependencies>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-API</artifactId>
<version>${project.version}</version>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
<scope>compile</scope>
</dependency>
<!-- Testing dependencies -->
<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.12</version>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.21.0</version>
<artifactId>mockito-all</artifactId>
<version>1.8.4</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -257,7 +261,7 @@
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
@@ -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();
}
}
@@ -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
*/
@@ -52,7 +52,7 @@ public interface AsynchronousManager {
/**
* Unregisters and closes the first asynchronous handler associated with the given listener.
* @param listener - asynchronous listener
* @param handler - asynchronous handler.
*/
public abstract void unregisterAsyncHandler(PacketListener listener);
@@ -63,7 +63,7 @@ public interface AsynchronousManager {
public void unregisterAsyncHandlers(Plugin plugin);
/**
* Retrieves a immutable set containing the ID of the sent server packets that will be
* 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.
@@ -73,7 +73,7 @@ public interface AsynchronousManager {
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.
*/
@@ -152,4 +152,4 @@ public interface AsynchronousManager {
* @return Set of every asynchronous packet listener.
*/
public abstract Set<PacketListener> getAsyncHandlers();
}
}
@@ -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
*/
@@ -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;
}
@@ -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)
);
}
@@ -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!");
}
}
@@ -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,7 +24,6 @@ import org.bukkit.entity.Player;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.netty.WirePacket;
/**
* Represents a object capable of sending or receiving packets.
@@ -38,7 +37,7 @@ public interface PacketStream {
* @param packet - packet to send.
* @throws InvocationTargetException - if an error occured when sending the packet.
*/
public void sendServerPacket(Player receiver, PacketContainer packet)
public void sendServerPacket(Player receiver, PacketContainer packet)
throws InvocationTargetException;
/**
@@ -62,22 +61,6 @@ public interface PacketStream {
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters)
throws InvocationTargetException;
/**
* Send a wire packet to the given player.
* @param receiver - the receiver.
* @param id - packet id.
* @param bytes - packet bytes.
* @throws InvocationTargetException if an error occured when sending the packet.
*/
public void sendWirePacket(Player receiver, int id, byte[] bytes) throws InvocationTargetException;
/**
* Send a wire packet to the given player.
* @param receiver - the receiver.
* @param packet - packet to send.
* @throws InvocationTargetException if an error occured when sending the packet.
*/
public void sendWirePacket(Player receiver, WirePacket packet) throws InvocationTargetException;
/**
* Simulate recieving a certain packet from a given player.
@@ -86,7 +69,7 @@ public interface PacketStream {
* @throws InvocationTargetException If the reflection machinery failed.
* @throws IllegalAccessException If the underlying method caused an error.
*/
public void recieveClientPacket(Player sender, PacketContainer packet)
public void recieveClientPacket(Player sender, PacketContainer packet)
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 + "]";
}
}
@@ -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,7 +15,7 @@ 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 = IntegerMap.newMap();
public final IntegerMap<PacketType> HANDSHAKE_SERVER = IntegerMap.newMap();
@@ -49,39 +47,6 @@ class PacketTypeLookup {
}
}
}
public static class ClassLookup {
// Unroll lookup for performance reasons
public final Map<String, PacketType> HANDSHAKE_CLIENT = new ConcurrentHashMap<String, PacketType>();
public final Map<String, PacketType> HANDSHAKE_SERVER = new ConcurrentHashMap<String, PacketType>();
public final Map<String, PacketType> GAME_CLIENT = new ConcurrentHashMap<String, PacketType>();
public final Map<String, PacketType> GAME_SERVER = new ConcurrentHashMap<String, PacketType>();
public final Map<String, PacketType> STATUS_CLIENT = new ConcurrentHashMap<String, PacketType>();
public final Map<String, PacketType> STATUS_SERVER = new ConcurrentHashMap<String, PacketType>();
public final Map<String, PacketType> LOGIN_CLIENT = new ConcurrentHashMap<String, PacketType>();
public final Map<String, PacketType> LOGIN_SERVER = new ConcurrentHashMap<String, PacketType>();
/**
* 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<PacketType>();
@@ -89,14 +54,11 @@ class PacketTypeLookup {
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.
@@ -117,9 +79,9 @@ class PacketTypeLookup {
}
// Skip unknown current packets
if (type.getCurrentId() != PacketType.UNKNOWN_PACKET) {
idLookup.getMap(type.getProtocol(), type.getSender()).put(type.getCurrentId(), type);
classLookup.getMap(type.getProtocol(), type.getSender()).put(type.getClassNames()[0], type);
currentLookup.getMap(type.getProtocol(), type.getSender()).put(type.getCurrentId(), type);
}
// Save name
nameLookup.put(type.name(), type);
}
return this;
@@ -170,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);
}
}
@@ -4,20 +4,19 @@ import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.ConnectionSide;
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.DiscreteDomains;
import com.google.common.collect.Lists;
import com.google.common.collect.Range;
import com.google.common.collect.Ranges;
import com.google.common.collect.Sets;
class PacketTypeParser {
public final static 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;
@@ -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) {
@@ -73,7 +72,7 @@ class PacketTypeParser {
}
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)) {
@@ -144,5 +143,5 @@ class PacketTypeParser {
return Protocol.STATUS;
else
return null;
}
}
}
@@ -17,6 +17,10 @@
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;
/**
@@ -161,7 +165,30 @@ public final class Packets {
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();
@@ -235,7 +262,26 @@ public final class Packets {
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();
@@ -1,4 +1,4 @@
/**
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
@@ -14,19 +14,18 @@
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.injector.PlayerInjectHooks;
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;
@@ -39,49 +38,53 @@ import com.google.common.io.Files;
*/
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 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.
*/
@@ -90,20 +93,19 @@ 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, Charsets.UTF_8));
@@ -116,40 +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())
dataFile.delete();
try {
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) {
@@ -158,29 +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)
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.
@@ -190,122 +185,93 @@ 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);
}
/**
* Set whether or not detailed error reporting is enabled.
*
* @param value - TRUE if it is enabled, FALSE otherwise.
*/
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) {
@@ -315,19 +281,17 @@ public class ProtocolConfig {
/**
* 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) {
@@ -337,20 +301,20 @@ public class ProtocolConfig {
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.
* 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.
*/
@@ -358,16 +322,15 @@ public class ProtocolConfig {
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>
@@ -379,31 +342,28 @@ public class ProtocolConfig {
setConfig(global, METRICS_ENABLED, enabled);
modCount++;
}
/**
* Retrieve whether or not the background compiler for structure modifiers is enabled or not.
*
* @return TRUE if it is enabled, FALSE otherwise.
*/
public boolean isBackgroundCompilerEnabled() {
return getGlobalValue(BACKGROUND_COMPILER_ENABLED, true);
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() {
@@ -413,81 +373,75 @@ 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(Locale.ENGLISH).replace(" ", "_"));
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.
*/
@@ -496,7 +450,7 @@ public class ProtocolConfig {
saveLastUpdate(lastUpdateTime);
if (configChanged)
plugin.saveConfig();
// And we're done
valuesChanged = false;
configChanged = false;
@@ -1,4 +1,4 @@
/**
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
@@ -14,6 +14,7 @@
* 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;
@@ -29,7 +30,6 @@ import java.util.regex.Pattern;
import org.bukkit.Server;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
@@ -44,11 +44,9 @@ 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.PlayerInjectHooks;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
import com.comphenix.protocol.metrics.Statistics;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.updater.Updater;
import com.comphenix.protocol.updater.Updater.UpdateType;
import com.comphenix.protocol.utility.ChatExtensions;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftVersion;
@@ -62,7 +60,7 @@ import com.google.common.util.concurrent.ListeningScheduledExecutorService;
*
* @author Kristian
*/
public class ProtocolLib extends JavaPlugin {
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.");
@@ -81,16 +79,26 @@ public class ProtocolLib extends JavaPlugin {
public static final ReportType REPORT_CANNOT_CREATE_TIMEOUT_TASK = new ReportType("Unable to create packet timeout task.");
public static final ReportType REPORT_CANNOT_UPDATE_PLUGIN = new ReportType("Cannot perform automatic updates.");
// Update information
static final String BUKKIT_DEV_SLUG = "protocollib";
static final int BUKKIT_DEV_ID = 45564;
/**
* 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,
LOGGING;
PROTOCOL
}
/**
@@ -131,28 +139,23 @@ public class ProtocolLib extends JavaPlugin {
// Settings/options
private int configExpectedMod = -1;
// Updater
private Updater updater;
public static boolean UPDATES_DISABLED;
// Logger
private static Logger logger;
private Logger logger;
private Handler redirectHandler;
// Commands
private CommandProtocol commandProtocol;
private CommandPacket commandPacket;
private CommandFilter commandFilter;
private PacketLogging packetLogging;
// Whether or not disable is not needed
private boolean skipDisable;
@Override
public void onLoad() {
// Logging
logger = getLogger();
ProtocolLogger.init(this);
// Load configuration
logger = getLoggerSafely();
Application.registerPrimaryThread();
// Initialize enhancer factory
EnhancerFactory.getInstance().setClassLoader(getClassLoader());
@@ -165,10 +168,6 @@ public class ProtocolLib extends JavaPlugin {
DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this);
reporter = getFilteredReporter(detailedReporter);
// Configuration
saveDefaultConfig();
reloadConfig();
try {
config = new ProtocolConfig(this);
} catch (Exception e) {
@@ -199,21 +198,8 @@ public class ProtocolLib extends JavaPlugin {
// Handle unexpected Minecraft versions
MinecraftVersion version = verifyMinecraftVersion();
// Set updater - this will not perform any update automatically
updater = Updater.create(this, BUKKIT_DEV_ID, getFile(), UpdateType.NO_DOWNLOAD, true);
unhookTask = new DelayedSingleTask(this);
protocolManager = PacketFilterManager.newBuilder()
.classLoader(getClassLoader())
.server(getServer())
.library(this)
.minecraftVersion(version)
.unhookTask(unhookTask)
.reporter(reporter)
.build();
// Initialize the API
ProtocolLibrary.init(this, config, protocolManager, reporter, executorAsync, executorSync);
protocolManager = PacketFilterManager.newBuilder().classLoader(getClassLoader()).server(getServer()).library(this).minecraftVersion(version).unhookTask(unhookTask).reporter(reporter).build();
// Setup error reporter
detailedReporter.addGlobalParameter("manager", protocolManager);
@@ -237,6 +223,8 @@ public class ProtocolLib extends JavaPlugin {
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_PLUGIN_LOAD_ERROR).error(e).callerParam(protocolManager));
disablePlugin();
@@ -252,7 +240,7 @@ public class ProtocolLib extends JavaPlugin {
try {
switch (command) {
case PROTOCOL:
commandProtocol = new CommandProtocol(reporter, this, updater, config);
commandProtocol = new CommandProtocol(reporter, this, config);
break;
case FILTER:
commandFilter = new CommandFilter(reporter, this, config);
@@ -260,17 +248,13 @@ public class ProtocolLib extends JavaPlugin {
case PACKET:
commandPacket = new CommandPacket(reporter, this, logger, commandFilter, protocolManager);
break;
case LOGGING:
packetLogging = new PacketLogging(this, protocolManager);
break;
}
} catch (OutOfMemoryError e) {
throw e;
} catch (LinkageError e) {
logger.warning("Failed to register command " + command.name() + ": " + 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));
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_REGISTER_COMMAND).messageParam(command.name(), e.getMessage()).error(e));
}
}
}
@@ -362,26 +346,19 @@ public class ProtocolLib extends JavaPlugin {
// 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! "
};
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;
}
// Check for incompatible plugins
checkForIncompatibility(manager);
// Initialize background compiler
if (backgroundCompiler == null && config.isBackgroundCompilerEnabled()) {
backgroundCompiler = new BackgroundCompiler(getClassLoader(), reporter);
@@ -396,7 +373,6 @@ public class ProtocolLib extends JavaPlugin {
registerCommand(CommandProtocol.NAME, commandProtocol);
registerCommand(CommandPacket.NAME, commandPacket);
registerCommand(CommandFilter.NAME, commandFilter);
registerCommand(PacketLogging.NAME, packetLogging);
// Player login and logout events
protocolManager.registerEvents(manager, this);
@@ -404,8 +380,11 @@ public class ProtocolLib extends JavaPlugin {
// 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();
@@ -419,6 +398,8 @@ public class ProtocolLib extends JavaPlugin {
}
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (IOException e) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_METRICS_IO_ERROR).error(e).callerParam(statistics));
} catch (Throwable e) {
@@ -426,28 +407,10 @@ public class ProtocolLib extends JavaPlugin {
}
}
// Plugin authors: Notify me to remove these
private void checkForIncompatibility(PluginManager manager) {
for (String plugin : ProtocolLibrary.INCOMPATIBLE) {
if (manager.getPlugin(plugin) != null) {
// Special case for TagAPI and iTag
if (plugin.equals("TagAPI")) {
Plugin iTag = manager.getPlugin("iTag");
if (iTag == null || iTag.getDescription().getVersion().startsWith("1.0")) {
logger.severe("Detected incompatible plugin: TagAPI");
}
} else {
logger.severe("Detected incompatible plugin: " + plugin);
}
}
}
}
// Used to check Minecraft version
private MinecraftVersion verifyMinecraftVersion() {
MinecraftVersion minimum = new MinecraftVersion(ProtocolLibrary.MINIMUM_MINECRAFT_VERSION);
MinecraftVersion maximum = new MinecraftVersion(ProtocolLibrary.MAXIMUM_MINECRAFT_VERSION);
MinecraftVersion minimum = new MinecraftVersion(MINIMUM_MINECRAFT_VERSION);
MinecraftVersion maximum = new MinecraftVersion(MAXIMUM_MINECRAFT_VERSION);
try {
MinecraftVersion current = new MinecraftVersion(getServer());
@@ -460,8 +423,8 @@ public class ProtocolLib extends JavaPlugin {
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));
@@ -472,7 +435,7 @@ public class ProtocolLib extends JavaPlugin {
private void checkConflictingVersions() {
Pattern ourPlugin = Pattern.compile("ProtocolLib-(.*)\\.jar");
MinecraftVersion currentVersion = new MinecraftVersion(getDescription().getVersion());
MinecraftVersion currentVersion = new MinecraftVersion(this.getDescription().getVersion());
MinecraftVersion newestVersion = null;
// Skip the file that contains this current instance however
@@ -480,29 +443,26 @@ public class ProtocolLib extends JavaPlugin {
try {
// Scan the plugin folder for newer versions of ProtocolLib
// The plugin folder isn't always plugins/
File pluginFolder = getDataFolder().getParentFile();
File pluginFolder = new File("plugins/");
File[] candidates = pluginFolder.listFiles();
if (candidates != null) {
for (File candidate : candidates) {
if (candidate.isFile() && !candidate.equals(loadedFile)) {
Matcher match = ourPlugin.matcher(candidate.getName());
if (match.matches()) {
MinecraftVersion version = new MinecraftVersion(match.group(1));
for (File candidate : pluginFolder.listFiles()) {
if (candidate.isFile() && !candidate.equals(loadedFile)) {
Matcher match = ourPlugin.matcher(candidate.getName());
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;
}
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) {
// TODO This shows [ProtocolLib] and [ProtocolLibrary] in the message
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_DETECT_CONFLICTING_PLUGINS).error(e));
}
@@ -511,9 +471,7 @@ public class ProtocolLib extends JavaPlugin {
// 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()));
throw new IllegalStateException(String.format("Detected a newer version of ProtocolLib (%s) in plugin folder than the current (%s). Disabling.", newestVersion.getVersion(), currentVersion.getVersion()));
}
}
@@ -526,11 +484,11 @@ public class ProtocolLib extends JavaPlugin {
PluginCommand command = getCommand(name);
// Try to load the command
if (command != null) {
if (command != null)
command.setExecutor(executor);
} else {
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));
}
@@ -559,15 +517,12 @@ public class ProtocolLib extends JavaPlugin {
// House keeping
updateConfiguration();
// Check for updates too
if (!UPDATES_DISABLED && (tickCounter % 20) == 0) {
checkUpdates();
}
}
}, 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));
@@ -584,29 +539,6 @@ public class ProtocolLib extends JavaPlugin {
}
}
private void checkUpdates() {
// Ignore milliseconds - it's pointless
long currentTime = System.currentTimeMillis() / MILLI_PER_SECOND;
try {
long updateTime = config.getAutoLastTime() + config.getAutoDelay();
// Should we update?
if (currentTime > updateTime && !updater.isChecking()) {
// Initiate the update as if it came from the console
if (config.isAutoDownload())
commandProtocol.updateVersion(getServer().getConsoleSender(), false);
else if (config.isAutoNotify())
commandProtocol.checkVersion(getServer().getConsoleSender(), false);
else
commandProtocol.updateFinished();
}
} catch (Exception e) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_UPDATE_PLUGIN).error(e));
UPDATES_DISABLED = true;
}
}
@Override
public void onDisable() {
if (skipDisable) {
@@ -644,6 +576,52 @@ public class ProtocolLib extends JavaPlugin {
reporter = new BasicErrorReporter();
}
// Get the Bukkit logger first, before we try to create our own
private Logger getLoggerSafely() {
Logger log = null;
try {
log = getLogger();
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
// Ignore
}
// Use the default logger instead
if (log == null)
log = Logger.getLogger("Minecraft");
return log;
}
/**
* Retrieve the 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>
@@ -655,7 +633,23 @@ public class ProtocolLib extends JavaPlugin {
return statistics;
}
public ProtocolConfig getProtocolConfig() {
return config;
/**
* 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;
}
}
@@ -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
*/
@@ -29,7 +29,6 @@ import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.async.AsyncMarker;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.PacketConstructor;
@@ -54,7 +53,7 @@ public interface ProtocolManager extends PacketStream {
/**
* Send a packet to the given player.
* <p>
* Re-sending a previously cancelled packet is discouraged. Use {@link AsyncMarker#incrementProcessingDelay()}
* 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.
@@ -63,13 +62,13 @@ public interface ProtocolManager extends PacketStream {
* @throws InvocationTargetException - if an error occurred when sending the packet.
*/
@Override
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters)
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()}
* 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.
@@ -79,7 +78,7 @@ public interface ProtocolManager extends PacketStream {
* @throws IllegalAccessException If the underlying method caused an error.
*/
@Override
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
throws IllegalAccessException, InvocationTargetException;
/**
@@ -90,9 +89,9 @@ public interface ProtocolManager extends PacketStream {
public void broadcastServerPacket(PacketContainer packet);
/**
* Broadcast a packet to every player that is receiving information about a given entity.
* 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
* 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.
@@ -116,17 +115,17 @@ public interface ProtocolManager extends PacketStream {
public ImmutableSet<PacketListener> getPacketListeners();
/**
* Adds a packet listener.
* 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
* 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.
* 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.
@@ -159,7 +158,7 @@ public interface ProtocolManager extends PacketStream {
/**
* 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
* 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.
@@ -176,7 +175,7 @@ public interface ProtocolManager extends PacketStream {
/**
* 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
* 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.
@@ -200,7 +199,7 @@ public interface ProtocolManager extends PacketStream {
/**
* Construct a packet using the special builtin Minecraft constructors.
* @param type - the packet type.
* @param id - the packet type.
* @param arguments - arguments that will be passed to the constructor.
* @return The packet constructor.
*/
@@ -209,7 +208,7 @@ public interface ProtocolManager extends PacketStream {
/**
* 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
* 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.
@@ -270,7 +269,7 @@ public interface ProtocolManager extends PacketStream {
public MinecraftVersion getMinecraftVersion();
/**
* Determines whether or not this protocol manager has been disabled.
* Determines whether or not this protocol manager has been disabled.
* @return TRUE if it has, FALSE otherwise.
*/
public boolean isClosed();
@@ -280,6 +279,4 @@ public interface ProtocolManager extends PacketStream {
* @return Asynchronous packet manager.
*/
public AsynchronousManager getAsynchronousManager();
public void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist);
}
}
@@ -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,9 +23,9 @@ import java.util.Arrays;
import java.util.Deque;
import java.util.List;
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.DiscreteDomains;
import com.google.common.collect.Range;
import com.google.common.collect.Ranges;
/**
* Used to parse ranges in CommandPacket.
@@ -68,7 +68,7 @@ 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 @@ class RangeParser {
} else {
// Just a single number
range = Range.singleton(Integer.parseInt(current));
range = Ranges.singleton(Integer.parseInt(current));
ranges.add(range);
}
@@ -102,7 +102,7 @@ class RangeParser {
// 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 @@ class RangeParser {
}
} else {
if (start >= 0) {
result.add(Range.closed(start, i - 1));
result.add(Ranges.closed(start, i - 1));
start = -1;
}
}
@@ -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;
}
@@ -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,6 +35,7 @@ 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;
@@ -80,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.
*/
@@ -147,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());
}
@@ -180,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);
}
@@ -220,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) {
@@ -231,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()) {
@@ -249,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
@@ -294,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());
@@ -304,28 +305,22 @@ 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 = newEvent.getPlayer();
if (player != null) {
// Start the process
getSendingQueue(syncPacket).enqueue(newEvent);
// We know this is occuring 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
@@ -367,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
@@ -471,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
@@ -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.
@@ -208,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() {
@@ -315,7 +315,7 @@ public class AsyncListenerHandler {
* public Void apply(&#64;Nullable AsyncRunnable workerLoop) {
* Thread thread = Thread.currentThread();
* int prevPriority = thread.getPriority();
*
*
* thread.setPriority(Thread.MIN_PRIORITY);
* workerLoop.run();
* thread.setPriority(prevPriority);
@@ -344,7 +344,8 @@ public class AsyncListenerHandler {
}
private void scheduleAsync(Runnable runnable) {
listener.getPlugin().getServer().getScheduler().runTaskAsynchronously(listener.getPlugin(), runnable);
// Handle deprecation
WrappedScheduler.runAsynchronouslyRepeat(listener.getPlugin(), filterManager.getScheduler(), runnable, 0L, -1L);
}
/**
@@ -356,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())
);
@@ -382,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.
@@ -398,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).
@@ -502,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.
@@ -562,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.");
@@ -583,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;
@@ -615,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);
@@ -623,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();
@@ -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,18 +24,14 @@ 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;
/**
@@ -110,7 +106,7 @@ 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");
@@ -127,11 +123,11 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
/**
* 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.
@@ -213,11 +209,11 @@ 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.
@@ -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.
@@ -401,11 +397,10 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
/**
* 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_.*");
@@ -420,36 +415,18 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
} else if (methods.size() == 1) {
// We're in 1.2.5
alwaysSync = true;
} else if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.BOUNTIFUL_UPDATE)) {
// 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);
@@ -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;
@@ -243,4 +243,4 @@ public class ExpireHashMap<K, V> {
return valueView.toString();
}
}
@@ -18,7 +18,6 @@ public class IntegerMap<T> {
/**
* Construct a new integer map.
* @param <T> Parameter type
* @return A new integer map.
*/
public static <T> IntegerMap<T> newMap() {
@@ -52,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)
@@ -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;
@@ -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.
@@ -6,11 +6,12 @@ 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.comphenix.protocol.utility.Util;
import com.google.common.base.Function;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.RemovalListener;
@@ -69,7 +70,6 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
/**
* 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() {
@@ -78,7 +78,6 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
/**
* 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() {
@@ -166,7 +165,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
* @return The player with the given key, or NULL if not found.
*/
protected Player findOnlinePlayer(Object key) {
for (Player player : Util.getOnlinePlayers()) {
for (Player player : Bukkit.getOnlinePlayers()) {
if (key.equals(keyMethod.apply(player))) {
return player;
}
@@ -178,6 +177,7 @@ 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
*/
protected Player lookupPlayer(Object key) {
try {
@@ -293,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() {
@@ -315,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();
}
@@ -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,7 +25,7 @@ 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.
* This class is intentionally missing a size method.
* @author Kristian
*/
public class IntegerSet {
@@ -67,7 +67,7 @@ public class IntegerSet {
/**
* Add the given element to the set, or do nothing if it already exists.
* @param element - element to add.
* @throws ArrayIndexOutOfBoundsException If the given element is not in the range [0, count).
* @throws OutOfBoundsException If the given element is not in the range [0, count).
*/
public void add(int element) {
array[element] = true;
@@ -65,7 +65,7 @@ public class PacketTypeSet {
/**
* Remove the given types from the set.
* @param types Types to remove
* @param type - the types to remove.
*/
public synchronized void removeAll(Iterable<? extends PacketType> types) {
for (PacketType type : types) {
@@ -101,7 +101,7 @@ public class PacketTypeSet {
}
/**
* Determine if the type of a packet is in the current set.
* Determine if the type of a packet is in the current set.
* @param packet - the packet.
* @return TRUE if it is, FALSE otherwise.
*/
@@ -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 @@ import org.apache.commons.lang.builder.ToStringStyle;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.collections.ExpireHashMap;
import com.comphenix.protocol.error.Report.ReportBuilder;
import com.comphenix.protocol.events.PacketAdapter;
@@ -50,13 +49,13 @@ import com.google.common.primitives.Primitives;
*/
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";
@@ -79,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;
@@ -93,7 +92,6 @@ public class DetailedErrorReporter implements ErrorReporter {
/**
* Create a default error reporting system.
* @param plugin - the plugin owner.
*/
public DetailedErrorReporter(Plugin plugin) {
this(plugin, DEFAULT_PREFIX, DEFAULT_SUPPORT_URL);
@@ -122,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 {
@@ -282,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
@@ -346,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);
@@ -356,7 +346,7 @@ public class DetailedErrorReporter implements ErrorReporter {
}
// Data dump!
writer.println("Dump:");
writer.println(" ===== DUMP =====");
// Relevant parameters
if (report.hasCallerParameters()) {
@@ -366,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));
}
@@ -379,18 +369,14 @@ 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) {
if (ERROR_PERMISSION != null) {
Bukkit.getServer().broadcast(
String.format("Error %s (%s) occured in %s.", report.getReportMessage(), report.getException(), sender),
ERROR_PERMISSION
@@ -455,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]";
@@ -464,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
@@ -489,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());
}
@@ -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
*/
@@ -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);
}
}
@@ -1,6 +1,7 @@
package com.comphenix.protocol.error;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.CodeSource;
@@ -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;
}
}
@@ -10,7 +10,7 @@ import javax.annotation.Nullable;
*
* @author Kristian
*/
public class Report {
public class Report {
private final ReportType type;
private final Throwable exception;
private final Object[] messageParameters;
@@ -76,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.
@@ -122,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);
}
@@ -135,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.");
@@ -157,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.
*/
@@ -231,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;
@@ -2,7 +2,6 @@ package com.comphenix.protocol.events;
import com.comphenix.protocol.injector.GamePhase;
/**
* Represents additional options a listener may require.
*
@@ -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);
@@ -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
*/
@@ -32,7 +32,7 @@ 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 {
/**
@@ -60,7 +60,7 @@ public class ListeningWhitelist {
* <p>
* Deprecated: Use {@link #newBuilder()} instead.
* @param priority - the listener priority.
* @param whitelist - set of IDs to observe/enable.
* @param whitelist - set of IDs to observe/enable.
*/
@Deprecated
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist) {
@@ -192,7 +192,7 @@ public class ListeningWhitelist {
/**
* 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.
* @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) {
@@ -238,7 +238,12 @@ public class ListeningWhitelist {
if (this == EMPTY_WHITELIST)
return "EMPTY_WHITELIST";
else
return "ListeningWhitelist[priority=" + priority + ", packets=" + types + ", gamephase=" + gamePhase + ", options=" + options + "]";
return Objects.toStringHelper(this).
add("priority", priority).
add("packets", types).
add("gamephase", gamePhase).
add("options", options).
toString();
}
/**
@@ -437,7 +442,7 @@ public class ListeningWhitelist {
/**
* 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) {
@@ -447,7 +452,7 @@ public class ListeningWhitelist {
/**
* 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) {
@@ -456,7 +461,7 @@ public class ListeningWhitelist {
/**
* 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) {
@@ -24,12 +24,12 @@ import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
/**
* Marker containing the serialized packet data seen from the network,
* 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 abstract class NetworkMarker {
public static class EmptyBufferMarker extends NetworkMarker {
public EmptyBufferMarker(@Nonnull ConnectionSide side) {
super(side, (byte[]) null, null);
@@ -48,7 +48,7 @@ public abstract class NetworkMarker {
@Override
protected DataInputStream addHeader(DataInputStream input, PacketType type) {
throw new IllegalStateException("Buffer is empty.");
}
}
}
// Custom network handler
@@ -68,9 +68,8 @@ public abstract class NetworkMarker {
/**
* Construct a new network marker.
* @param side - which side this marker belongs to.
* @param side - whether or not this marker belongs to a client or server packet.
* @param inputBuffer - the read serialized packet data.
* @param type - packet type
*/
public NetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer, PacketType type) {
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
@@ -82,9 +81,9 @@ public abstract class NetworkMarker {
* Construct a new network marker.
* <p>
* The input buffer is only non-null for client-side packets.
* @param side - which side this marker belongs to.
* @param side - whether or not this marker belongs to a client or server packet.
* @param inputBuffer - the read serialized packet data.
* @param type - packet type
* @param handler - handle skipping headers.
*/
public NetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer, PacketType type) {
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
@@ -116,7 +115,7 @@ public abstract class NetworkMarker {
/**
* 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
* 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.
@@ -129,7 +128,7 @@ public abstract class NetworkMarker {
/**
* 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
* 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.
@@ -144,7 +143,7 @@ public abstract class NetworkMarker {
ByteBuffer result = inputBuffer.asReadOnlyBuffer();
try {
if (excludeHeader)
if (excludeHeader)
result = skipHeader(result);
else
result = addHeader(result, type);
@@ -159,7 +158,7 @@ public abstract class NetworkMarker {
/**
* 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()}.
* 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.
*/
@@ -170,7 +169,7 @@ public abstract class NetworkMarker {
/**
* Retrieve the serialized packet data as an input stream.
* <p>
* The data is exactly the same as in {@link #getInputBuffer()}.
* 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.
@@ -187,7 +186,7 @@ public abstract class NetworkMarker {
);
try {
if (excludeHeader)
if (excludeHeader)
input = skipHeader(input);
else
input = addHeader(input, type);
@@ -208,7 +207,7 @@ public abstract class NetworkMarker {
/**
* 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.
* 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.
@@ -262,10 +261,10 @@ public abstract class NetworkMarker {
}
/**
* Add a listener that is invoked after a packet has been successfully sent to the client, or received
* by the server.
* 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
* 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>
@@ -325,9 +324,7 @@ public abstract class NetworkMarker {
* Return a byte buffer without the header in the current packet.
* <p>
* It's safe to modify the position of the buffer.
* @param buffer - a read-only byte source.
* @return A byte buffer without the header in the current packet.
* @throws IOException If integer reading fails
* @param buffer - a read-only byte source.
*/
protected ByteBuffer skipHeader(ByteBuffer buffer) throws IOException {
skipHeader(new DataInputStream(new ByteBufferInputStream(buffer)));
@@ -338,16 +335,13 @@ public abstract class NetworkMarker {
* Return an input stream without the header in the current packet.
* <p>
* It's safe to modify the input stream.
* @param input - input stream
* @return An input stream without the header
* @throws IOException If integer reading fails
*/
protected abstract DataInputStream skipHeader(DataInputStream input) throws IOException;
/**
* Return the byte buffer prepended with the packet header.
* @param buffer - the read-only byte buffer.
* @param type - the current packet.
* @param buffer - the read-only byte buffer.
* @param type - the current packet.
* @return The byte buffer.
*/
protected abstract ByteBuffer addHeader(ByteBuffer buffer, PacketType type);
@@ -355,7 +349,7 @@ public abstract class NetworkMarker {
/**
* Return the input stream prepended with the packet header.
* @param input - the input stream.
* @param type - the current packet.
* @param type - the current packet.
* @return The byte buffer.
*/
protected abstract DataInputStream addHeader(DataInputStream input, PacketType type);
@@ -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
*/
@@ -50,7 +50,7 @@ public abstract class PacketAdapter implements PacketListener {
*/
public PacketAdapter(@Nonnull AdapterParameteters params) {
this(
checkValidity(params).plugin, params.connectionSide, params.listenerPriority,
checkValidity(params).plugin, params.connectionSide, params.listenerPriority,
params.gamePhase, params.options, params.packets
);
}
@@ -58,6 +58,7 @@ public abstract class PacketAdapter implements PacketListener {
/**
* 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) {
@@ -240,7 +241,7 @@ public abstract class PacketAdapter implements PacketListener {
*/
@Deprecated
public PacketAdapter(
Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority,
Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority,
GamePhase gamePhase, ListenerOptions[] options, Integer... packets) {
this(plugin, connectionSide, listenerPriority, gamePhase, options,
@@ -250,7 +251,7 @@ public abstract class PacketAdapter implements PacketListener {
// For internal use only
private PacketAdapter(
Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority,
Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority,
GamePhase gamePhase, ListenerOptions[] options, PacketType... packets) {
if (plugin == null)
@@ -271,7 +272,7 @@ public abstract class PacketAdapter implements PacketListener {
// Special case that allows us to specify optionIntercept().
if (connectionSide == ConnectionSide.BOTH) {
serverOptions = except(serverOptions, new ListenerOptions[0],
serverOptions = except(serverOptions, new ListenerOptions[0],
ListenerOptions.INTERCEPT_INPUT_BUFFER);
}
@@ -346,21 +347,23 @@ public abstract class PacketAdapter implements PacketListener {
* @return Name of the given plugin.
*/
public static String getPluginName(Plugin plugin) {
if (plugin == null)
return "UNKNOWN";
// Try to get the plugin name
try {
return plugin.getName();
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),
public String toString() {
// This is used by the error reporter
return String.format("PacketAdapter[plugin=%s, sending=%s, receiving=%s]",
getPluginName(this),
sendingWhitelist,
receivingWhitelist);
}
@@ -438,7 +441,7 @@ public abstract class PacketAdapter implements PacketListener {
}
/**
* Set this adapter to also look for client-side packets.
* Set this adapter to also look for client-side packets.
* @return This builder, for chaining.
*/
public AdapterParameteters clientSide() {
@@ -446,7 +449,7 @@ public abstract class PacketAdapter implements PacketListener {
}
/**
* Set this adapter to also look for server-side packets.
* Set this adapter to also look for server-side packets.
* @return This builder, for chaining.
*/
public AdapterParameteters serverSide() {
@@ -454,7 +457,7 @@ public abstract class PacketAdapter implements PacketListener {
}
/**
* Set the the event priority, where the execution is in ascending order from lowest to highest.
* 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.
@@ -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;
}
}
}
@@ -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,17 +23,15 @@ import java.io.ObjectOutputStream;
import java.lang.ref.WeakReference;
import java.util.EventObject;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import com.comphenix.protocol.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.server.TemporaryPlayer;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
@@ -41,7 +39,7 @@ import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
/**
* Represents a packet sending or receiving event. Changes to the packet will be
* 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
*/
@@ -49,7 +47,7 @@ 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 =
private static final SetMultimap<PacketType, PacketType> CHANGE_WARNINGS =
Multimaps.synchronizedSetMultimap(HashMultimap.<PacketType, PacketType>create());
/**
@@ -204,7 +202,7 @@ public class PacketEvent extends EventObject implements Cancellable {
* @return TRUE if we are, FALSE otherwise.
*/
public boolean isAsync() {
return !Bukkit.isPrimaryThread();
return !Application.isPrimaryThread();
}
/**
@@ -231,9 +229,9 @@ public class PacketEvent extends EventObject implements Cancellable {
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).
messageParam(oldType, newType).
build());
}
}
@@ -263,7 +261,6 @@ public class PacketEvent extends EventObject implements Cancellable {
* Retrieves whether or not the packet should be cancelled.
* @return TRUE if it should be cancelled, FALSE otherwise.
*/
@Override
public boolean isCancelled() {
return cancel;
}
@@ -310,7 +307,6 @@ public class PacketEvent extends EventObject implements Cancellable {
*
* @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.");
@@ -322,41 +318,7 @@ public class PacketEvent extends EventObject implements Cancellable {
* @return The player associated with this event.
*/
public Player getPlayer() {
Player player = playerReference.get();
// Check if the player has been updated so we can do more stuff
if (player instanceof TemporaryPlayer) {
Player updated = player.getPlayer();
if (updated != null && !(updated instanceof TemporaryPlayer)) {
playerReference.clear();
playerReference = new WeakReference<>(updated);
return updated;
}
}
return player;
}
/**
* Whether or not the player in this PacketEvent is temporary (i.e. they don't have a true player instance yet).
* Temporary players have a limited subset of methods that may be used:
* <ul>
* <li>getPlayer</li>
* <li>getAddress</li>
* <li>getServer</li>
* <li>chat</li>
* <li>sendMessage</li>
* <li>kickPlayer</li>
* <li>isOnline</li>
* </ul>
*
* Anything else will throw an UnsupportedOperationException. Use this check before calling other methods when
* dealing with packets early in the login sequence or if you get the aforementioned exception.
*
* @return True if the player is temporary, false if not.
*/
public boolean isPlayerTemporary() {
return getPlayer() instanceof TemporaryPlayer;
return playerReference.get();
}
/**
@@ -392,12 +354,12 @@ public class PacketEvent extends EventObject implements Cancellable {
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.
* <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.
*/
@@ -463,7 +425,7 @@ public class PacketEvent extends EventObject implements Cancellable {
}
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)
@@ -477,15 +439,10 @@ public class PacketEvent extends EventObject implements Cancellable {
final SerializedOfflinePlayer serialized = (SerializedOfflinePlayer) input.readObject();
// Better than nothing
if (serialized != null) {
if (serialized != null) {
// Store it, to prevent weak reference from cleaning up the reference
offlinePlayer = serialized.getPlayer();
playerReference = new WeakReference<Player>(offlinePlayer);
}
}
@Override
public String toString() {
return "PacketEvent[player=" + getPlayer() + ", packet=" + packet + "]";
}
}
@@ -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
*/
@@ -34,7 +34,7 @@ public interface PacketListener {
* <p>
* This method is executed on the main thread in 1.6.4 and earlier, and thus the Bukkit API is safe to use.
* <p>
* In Minecraft 1.7.2 and later, this method MAY be executed asynchronously, but only if {@link ListenerOptions#ASYNC}
* 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.
*/
@@ -43,9 +43,9 @@ public interface PacketListener {
/**
* Invoked right before a received packet from a client is being processed.
* <p>
* <b>WARNING</b>: <br>
* This method will be called <i>asynchronously</i>! You should synchronize with the main
* thread using {@link org.bukkit.scheduler.BukkitScheduler#scheduleSyncDelayedTask(Plugin, Runnable, long) scheduleSyncDelayedTask}
* <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.
*/
@@ -7,6 +7,7 @@ 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;
/**
@@ -137,6 +138,10 @@ public class ScheduledPacket {
@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();
}
}
@@ -139,6 +139,7 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return banned;
}
@Override
public void setBanned(boolean banned) {
this.banned = banned;
}
@@ -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,9 +24,6 @@ import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
@@ -34,7 +31,6 @@ import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.PacketConstructor.Unwrapper;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.primitives.Primitives;
/**
@@ -42,8 +38,8 @@ import com.google.common.primitives.Primitives;
* <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
@@ -95,7 +91,7 @@ public class BukkitUnwrapper implements Unwrapper {
@Override
public Object unwrapItem(Object wrappedObject) {
// Special case
if (wrappedObject == null)
if (wrappedObject == null)
return null;
Class<?> currentClass = PacketConstructor.getClass(wrappedObject);
@@ -153,7 +149,7 @@ public class BukkitUnwrapper implements Unwrapper {
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) {
@@ -163,7 +159,7 @@ public class BukkitUnwrapper implements Unwrapper {
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) {
@@ -182,59 +178,24 @@ public class BukkitUnwrapper implements Unwrapper {
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)
return fieldUnwrapper;
else
reporter.reportDetailed(this,
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_FIND_UNWRAP_METHOD).error(e).callerParam(type));
}
// Default method
return null;
}
// Players should /always/ be able to be unwrapped
// We should only get here if the 'Player' is a proxy
private Unwrapper getProxyUnwrapper(final Class<?> type) {
try {
if (Player.class.isAssignableFrom(type)) {
final Method getHandle = MinecraftReflection.getCraftPlayerClass().getMethod("getHandle");
Unwrapper unwrapper = new Unwrapper() {
@Override
public Object unwrapItem(Object wrapped) {
try {
return getHandle.invoke(((Player) wrapped).getPlayer());
} catch (Throwable ex) {
try {
return getHandle.invoke(Bukkit.getPlayer(((Player) wrapped).getUniqueId()));
} catch (ReflectiveOperationException ex1) {
throw new RuntimeException("Failed to unwrap proxy " + wrapped, ex);
}
}
}
};
unwrapperCache.put(type, unwrapper);
return unwrapper;
}
} catch (Throwable ignored) {
}
return null;
}
/**
* Retrieve a cached unwrapper using the handle field.
* @param type - a cached field unwrapper.
@@ -253,7 +214,7 @@ public class BukkitUnwrapper implements Unwrapper {
return checkClass((Class<?>) wrappedObject, type, find.getType());
return FieldUtils.readField(find, wrappedObject, true);
} catch (IllegalAccessException e) {
reporter.reportDetailed(this,
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_READ_FIELD_HANDLE).error(e).callerParam(wrappedObject, find)
);
return null;
@@ -266,7 +227,7 @@ public class BukkitUnwrapper implements Unwrapper {
} else {
// Inform about this too
reporter.reportDetailed(this,
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_READ_FIELD_HANDLE).callerParam(find)
);
return null;
@@ -22,12 +22,10 @@ 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.ListeningWhitelist;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.WirePacket;
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;
@@ -46,7 +44,6 @@ import com.google.common.collect.Sets;
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_SEND_QUEUED_WIRE_PACKET = new ReportType("Cannot send queued wire packet %s.");
public static final ReportType REPORT_CANNOT_REGISTER_QUEUED_LISTENER = new ReportType("Cannot register queued listener %s.");
private volatile InternalManager delegate;
@@ -106,7 +103,7 @@ public class DelayedPacketManager implements InternalManager {
/**
* Update the delegate to the underlying manager.
* <p>
* This will prompt this packet manager to immediately transmit and
* This will prompt this packet manager to immediately transmit and
* register all queued packets an listeners.
* @param delegate - delegate to the new manager.
*/
@@ -132,7 +129,7 @@ public class DelayedPacketManager implements InternalManager {
delegate.addPacketListener(listener);
} catch (IllegalArgumentException e) {
// Inform about this plugin error
reporter.reportWarning(this,
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_REGISTER_QUEUED_LISTENER).
callerParam(delegate).messageParam(listener).error(e));
}
@@ -152,7 +149,7 @@ public class DelayedPacketManager implements InternalManager {
}
}
private Runnable queuedAddPacket(final ConnectionSide side, final Player player, final PacketContainer packet,
private Runnable queuedAddPacket(final ConnectionSide side, final Player player, final PacketContainer packet,
final NetworkMarker marker, final boolean filtered) {
return new Runnable() {
@@ -172,10 +169,10 @@ public class DelayedPacketManager implements InternalManager {
}
} catch (Exception e) {
// Inform about this plugin error
reporter.reportWarning(this,
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_SEND_QUEUED_PACKET).
callerParam(delegate).messageParam(packet).error(e));
}
}
}
};
}
@@ -209,36 +206,6 @@ public class DelayedPacketManager implements InternalManager {
}
}
@Override
public void sendWirePacket(Player receiver, int id, byte[] bytes) throws InvocationTargetException {
WirePacket packet = new WirePacket(id, bytes);
sendWirePacket(receiver, packet);
}
@Override
public void sendWirePacket(final Player receiver, final WirePacket packet) throws InvocationTargetException {
if (delegate != null) {
delegate.sendWirePacket(receiver, packet);
} else {
queuedActions.add(new Runnable() {
@Override
public void run() {
try {
delegate.sendWirePacket(receiver, packet);
} catch (Throwable ex) {
// Inform about this plugin error
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_SEND_QUEUED_WIRE_PACKET)
.callerParam(delegate)
.messageParam(packet)
.error(ex));
}
}
});
}
}
@Override
public void recieveClientPacket(Player sender, PacketContainer packet) throws IllegalAccessException, InvocationTargetException {
recieveClientPacket(sender, packet, null, true);
@@ -437,7 +404,7 @@ public class DelayedPacketManager implements InternalManager {
@Override
public void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
if (delegate != null)
if (delegate != null)
delegate.updateEntity(entity, observers);
else
EntityUtilities.updateEntity(entity, observers);
@@ -510,15 +477,4 @@ public class DelayedPacketManager implements InternalManager {
delegate.close();
closed = true;
}
@Override
public void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist) {
for (PacketType type : whitelist.getTypes()) {
if (type == null) {
throw new IllegalArgumentException(String.format("Packet type in in listener %s was NULL.",
PacketAdapter.getPluginName(listener))
);
}
}
}
}
@@ -2,30 +2,32 @@
* 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.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@@ -51,45 +53,74 @@ class EntityUtilities {
private static Method scanPlayersMethod;
/*
public static void updateEntity2(Entity entity, List<Player> observers) {
EntityTrackerEntry entry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
List<EntityPlayer> nmsPlayers = getNmsPlayers(observers);
entry.trackedPlayers.removeAll(nmsPlayers);
entry.scanPlayers(nmsPlayers);
}
*/
public static void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
if (entity == null || !entity.isValid()) {
return;
// 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 + ".");
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 = getTrackedPlayers(trackedPlayersField, trackerEntry);
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.
// TODO: Make sure this stays up to date with version changes - 1.8 - 1.10
if (scanPlayersMethod == null) {
scanPlayersMethod = trackerEntry.getClass().getMethod("scanPlayers", List.class);
}
scanPlayersMethod.invoke(trackerEntry, nmsPlayers);
scanPlayersMethod.invoke(trackerEntry, nmsPlayers);
} catch (IllegalArgumentException e) {
throw e;
} catch (IllegalAccessException e) {
@@ -110,63 +141,43 @@ class EntityUtilities {
* @throws FieldAccessException If reflection failed.
*/
public static List<Player> getEntityTrackers(Entity entity) {
if (entity == null || !entity.isValid()) {
return new ArrayList<>();
}
try {
List<Player> result = new ArrayList<Player>();
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
if (trackerEntry == null) {
throw new IllegalArgumentException("Cannot find entity trackers for " + entity + ".");
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 = getTrackedPlayers(trackedPlayersField, trackerEntry);
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);
}
}
// Damn you, Paper
private static Collection<?> getTrackedPlayers(Field field, Object entry) throws IllegalAccessException {
Validate.notNull(field, "Cannot find 'trackedPlayers' field.");
Validate.notNull(entry, "entry cannot be null!");
Object value = FieldUtils.readField(field, entry, false);
if (value instanceof Collection) {
return (Collection<?>) value;
} else if (value instanceof Map) {
return ((Map<?, ?>) value).keySet();
} else {
// Please. No more changes.
throw new IllegalStateException("trackedPlayers field was an unknown type: expected Collection or Map, but got " + value.getClass());
}
}
/*
private static EntityTrackerEntry getEntityTrackerEntry2(World world, int entityID) {
WorldServer worldServer = ((CraftWorld) world).getHandle();
EntityTracker tracker = worldServer.tracker;
return tracker.trackedEntities.get(entityID);
}
*/
private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException {
/**
* 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);
@@ -182,28 +193,39 @@ class EntityUtilities {
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot access 'tracker' field due to security limitations.", e);
}
// Looking for an IntHashMap in the tracker entry
if (trackedEntitiesField == null) {
trackedEntitiesField = FuzzyReflection.fromObject(tracker, false)
.getFieldByType("trackedEntities", MinecraftReflection.getIntHashMapClass());
@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 map
// Read the entity hashmap
Object trackedEntities = null;
try {
trackedEntities = FieldUtils.readField(trackedEntitiesField, tracker, false);
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 associated entity.
* @return The asssociated entity.
* @throws FieldAccessException Reflection error.
*/
public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException {
@@ -215,29 +237,30 @@ class EntityUtilities {
if (trackerEntry != null) {
if (trackerField == null) {
try {
Class<?> entryClass = MinecraftReflection.getMinecraftClass("EntityTrackerEntry");
trackerField = entryClass.getDeclaredField("tracker");
trackerField = trackerEntry.getClass().getField("tracker");
} catch (NoSuchFieldException e) {
// Assume it's the first entity field then
trackerField = FuzzyReflection.fromObject(trackerEntry, true)
.getFieldByType("tracker", MinecraftReflection.getEntityClass());
// 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();
@@ -4,6 +4,7 @@ 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.
@@ -91,4 +91,4 @@ public interface ListenerInvoker {
*/
@Deprecated
public abstract Class<?> getPacketClassFromID(int packetID, boolean forceVanilla);
}
}
@@ -28,30 +28,29 @@ class LoginPackets {
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
// MCPC+/Cauldron contains Forge, which uses CUSTOM_PAYLOAD during login
if (version.isAtLeast(MinecraftVersion.HORSE_UPDATE) || isCauldronOrMCPC()) {
if (version.compareTo(MinecraftVersion.HORSE_UPDATE) >= 0 || triesForgeIntegration()) {
clientSide.add(Packets.Client.CUSTOM_PAYLOAD);
}
if (isCauldronOrMCPC()) {
serverSide.add(Packets.Server.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 running MCPC or Cauldron.
* Determine if we are runnign MCPC.
* @return TRUE if we are, FALSE otherwise.
*/
private static boolean isCauldronOrMCPC() {
String version = Bukkit.getServer().getVersion();
return version.contains("MCPC") || version.contains("Cauldron");
private static boolean triesForgeIntegration() {
String version = Bukkit.getVersion();
return version.contains("MCPC-Plus") || version.contains("Cauldron") || version.contains("Thermos");
}
/**
@@ -36,7 +36,7 @@ public class NetworkProcessor {
*/
public byte[] processOutput(PacketEvent event, NetworkMarker marker, final byte[] input) {
// Bit of a hack - but we need the performance
PriorityQueue<PacketOutputHandler> handlers = (PriorityQueue<PacketOutputHandler>)
PriorityQueue<PacketOutputHandler> handlers = (PriorityQueue<PacketOutputHandler>)
marker.getOutputHandlers();
byte[] output = input;
@@ -66,7 +66,6 @@ public class NetworkProcessor {
/**
* Invoke the post listeners and packet transmission, if any.
* @param event - PacketEvent
* @param marker - the network marker, or NULL.
*/
public void invokePostEvent(PacketEvent event, NetworkMarker marker) {
@@ -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
*/
@@ -38,7 +38,7 @@ import com.google.common.primitives.Primitives;
*/
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().
*/
@@ -59,7 +59,7 @@ public class PacketConstructor {
private PacketConstructor(Constructor<?> constructorMethod) {
this.constructorMethod = constructorMethod;
this.unwrappers = Lists.newArrayList((Unwrapper) new BukkitUnwrapper(new RethrowErrorReporter() ));
this.unwrappers.addAll(BukkitConverters.getUnwrappers());
this.unwrappers.addAll(BukkitConverters.getUnwrappers());
}
private PacketConstructor(PacketType type, Constructor<?> constructorMethod, List<Unwrapper> unwrappers, Unwrapper[] paramUnwrapper) {
@@ -104,7 +104,7 @@ public class PacketConstructor {
/**
* 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.
* 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.
@@ -120,7 +120,7 @@ public class PacketConstructor {
/**
* Create a packet constructor that creates packets using the given types.
* <p>
* Note that if you pass a Class as a value, it will use its type directly.
* 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.
@@ -129,7 +129,7 @@ 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
@@ -258,7 +258,7 @@ public class PacketConstructor {
/**
* 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
* 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.
@@ -18,45 +18,19 @@
package com.comphenix.protocol.injector;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.async.AsyncMarker;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.*;
import com.comphenix.protocol.injector.netty.ProtocolInjector;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
import com.comphenix.protocol.injector.packet.PacketInjector;
import com.comphenix.protocol.injector.packet.PacketInjectorBuilder;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.ConflictStrategy;
import com.comphenix.protocol.injector.player.PlayerInjector.ServerHandlerNull;
import com.comphenix.protocol.injector.player.PlayerInjectorBuilder;
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.Util;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import io.netty.channel.Channel;
import 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;
@@ -74,6 +48,45 @@ import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.async.AsyncMarker;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.ListenerOptions;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.NettyProtocolInjector;
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
import com.comphenix.protocol.injector.packet.PacketInjector;
import com.comphenix.protocol.injector.packet.PacketInjectorBuilder;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.ConflictStrategy;
import com.comphenix.protocol.injector.player.PlayerInjector.ServerHandlerNull;
import com.comphenix.protocol.injector.player.PlayerInjectorBuilder;
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.BukkitUtil;
import com.comphenix.protocol.utility.EnhancerFactory;
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.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
public final class PacketFilterManager implements ListenerInvoker, InternalManager {
public static final ReportType REPORT_CANNOT_LOAD_PACKET_LIST = new ReportType("Cannot load server and client packet list.");
@@ -83,8 +96,8 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
new ReportType("%s doesn't depend on ProtocolLib. Check that its plugin.yml has a 'depend' directive.");
// Registering packet IDs that are not supported
public static final ReportType REPORT_UNSUPPORTED_SERVER_PACKET = new ReportType("[%s] Unsupported server packet in current Minecraft version: %s");
public static final ReportType REPORT_UNSUPPORTED_CLIENT_PACKET = new ReportType("[%s] Unsupported client packet in current Minecraft version: %s");
public static final ReportType REPORT_UNSUPPORTED_SERVER_PACKET_ID = new ReportType("[%s] Unsupported server packet ID in current Minecraft version: %s");
public static final ReportType REPORT_UNSUPPORTED_CLIENT_PACKET_ID = new ReportType("[%s] Unsupported client packet ID in current Minecraft version: %s");
// Problems injecting and uninjecting players
public static final ReportType REPORT_CANNOT_UNINJECT_PLAYER = new ReportType("Unable to uninject net handler for player.");
@@ -99,6 +112,36 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
*/
public static final int TICKS_PER_SECOND = 20;
/**
* Sets the inject hook type. Different types allow for maximum compatibility.
* @author Kristian
*/
public enum PlayerInjectHooks {
/**
* The injection hook that does nothing. Set when every other inject hook fails.
*/
NONE,
/**
* Override the network handler object itself. Only works in 1.3.
* <p>
* Cannot intercept MapChunk packets.
*/
NETWORK_MANAGER_OBJECT,
/**
* Override the packet queue lists in NetworkHandler.
* <p>
* Cannot intercept MapChunk packets.
*/
NETWORK_HANDLER_FIELDS,
/**
* Override the server handler object. Versatile, but a tad slower.
*/
NETWORK_SERVER_OBJECT;
}
// The amount of time to wait until we actually unhook every player
private static final int UNHOOK_DELAY = 5 * TICKS_PER_SECOND;
@@ -158,7 +201,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
private SpigotPacketInjector spigotInjector;
// Netty injector (for 1.7.2)
private ProtocolInjector nettyInjector;
private NettyProtocolInjector nettyInjector;
// Plugin verifier
private PluginVerifier pluginVerifier;
@@ -176,8 +219,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
private boolean debug;
/**
* Only create instances of this class if ProtocolLib is disabled.
* @param builder - PacketFilterBuilder
* Only create instances of this class if protocol lib is disabled.
*/
public PacketFilterManager(PacketFilterBuilder builder) {
// Used to determine if injection is needed
@@ -210,6 +252,8 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
this.pluginVerifier = new PluginVerifier(builder.getLibrary());
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
reporter.reportWarning(this, Report.newBuilder(REPORT_PLUGIN_VERIFIER_ERROR).
messageParam(e.getMessage()).error(e));
@@ -224,7 +268,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
// Use the correct injection type
if (MinecraftReflection.isUsingNetty()) {
this.nettyInjector = new ProtocolInjector(builder.getLibrary(), this, reporter);
this.nettyInjector = new NettyProtocolInjector(builder.getLibrary(), this, reporter);
this.playerInjection = nettyInjector.getPlayerInjector();
this.packetInjector = nettyInjector.getPacketInjector();
@@ -393,7 +437,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
playerInjection.checkListener(listener);
}
if (hasSending)
incrementPhases(processPhase(sending));
incrementPhases(processPhase(sending, ConnectionSide.SERVER_SIDE));
// Handle receivers after senders
if (hasReceiving) {
@@ -402,7 +446,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
enablePacketFilters(listener, receiving.getTypes());
}
if (hasReceiving)
incrementPhases(processPhase(receiving));
incrementPhases(processPhase(receiving, ConnectionSide.CLIENT_SIDE));
// Inform our injected hooks
packetListeners.add(listener);
@@ -410,7 +454,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
}
}
private GamePhase processPhase(ListeningWhitelist whitelist) {
private GamePhase processPhase(ListeningWhitelist whitelist, ConnectionSide side) {
// Determine if this is a login packet, ensuring that gamephase detection is enabled
if (!whitelist.getGamePhase().hasLogin() &&
!whitelist.getOptions().contains(ListenerOptions.DISABLE_GAMEPHASE_DETECTION)) {
@@ -435,7 +479,9 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
// We only check the recieving whitelist
if (whitelist.getOptions().contains(ListenerOptions.INTERCEPT_INPUT_BUFFER)) {
updated.addAll(whitelist.getTypes());
for (PacketType type : whitelist.getTypes()) {
updated.add(type);
}
}
}
// Update it
@@ -459,7 +505,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
unhookTask.cancel();
else
// Inject our hook into already existing players
initializePlayers(Util.getOnlinePlayers());
initializePlayers(BukkitUtil.getOnlinePlayers());
}
}
}
@@ -480,7 +526,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
@Override
public void run() {
// Inject our hook into already existing players
uninitializePlayers(Util.getOnlinePlayers());
uninitializePlayers(BukkitUtil.getOnlinePlayers());
}
});
}
@@ -493,8 +539,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
* @param whitelist - whitelist of packet IDs.
* @throws IllegalArgumentException If the whitelist is illegal.
*/
@Override
public void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist) {
public static void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist) {
for (PacketType type : whitelist.getTypes()) {
if (type == null) {
throw new IllegalArgumentException(String.format("Packet type in in listener %s was NULL.",
@@ -522,11 +567,11 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
// Remove listeners and phases
if (sending != null && sending.isEnabled()) {
sendingRemoved = sendingListeners.removeListener(listener, sending);
decrementPhases(processPhase(sending));
decrementPhases(processPhase(sending, ConnectionSide.SERVER_SIDE));
}
if (receiving != null && receiving.isEnabled()) {
receivingRemoved = recievedListeners.removeListener(listener, receiving);
decrementPhases(processPhase(receiving));
decrementPhases(processPhase(receiving, ConnectionSide.CLIENT_SIDE));
}
// Remove hooks, if needed
@@ -610,6 +655,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
* Note that all packets are disabled by default.
*
* @param listener - the listener that requested to enable these filters.
* @param side - which side the event will arrive from.
* @param packets - the packet id(s).
*/
private void enablePacketFilters(PacketListener listener, Iterable<PacketType> packets) {
@@ -624,7 +670,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
playerInjection.addPacketHandler(type, listener.getSendingWhitelist().getOptions());
else
reporter.reportWarning(this,
Report.newBuilder(REPORT_UNSUPPORTED_SERVER_PACKET).messageParam(PacketAdapter.getPluginName(listener), type)
Report.newBuilder(REPORT_UNSUPPORTED_SERVER_PACKET_ID).messageParam(PacketAdapter.getPluginName(listener), type)
);
}
@@ -634,7 +680,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
packetInjector.addPacketHandler(type, listener.getReceivingWhitelist().getOptions());
else
reporter.reportWarning(this,
Report.newBuilder(REPORT_UNSUPPORTED_CLIENT_PACKET).messageParam(PacketAdapter.getPluginName(listener), type)
Report.newBuilder(REPORT_UNSUPPORTED_CLIENT_PACKET_ID).messageParam(PacketAdapter.getPluginName(listener), type)
);
}
}
@@ -660,7 +706,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
@Override
public void broadcastServerPacket(PacketContainer packet) {
Preconditions.checkNotNull(packet, "packet cannot be NULL.");
broadcastServerPacket(packet, Util.getOnlinePlayers());
broadcastServerPacket(packet, BukkitUtil.getOnlinePlayers());
}
@Override
@@ -686,7 +732,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
Location recycle = origin.clone();
// Only broadcast the packet to nearby players
for (Player player : Util.getOnlinePlayers()) {
for (Player player : server.getOnlinePlayers()) {
if (world.equals(player.getWorld()) &&
getDistanceSquared(origin, recycle, player) <= maxDistance) {
@@ -760,7 +806,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
if (!filters) {
// We may have to delay the packet due to non-asynchronous monitor listeners
if (!Bukkit.isPrimaryThread() && playerInjection.hasMainThreadListener(packet.getType())) {
if (!filters && !Bukkit.isPrimaryThread() && playerInjection.hasMainThreadListener(packet.getType())) {
final NetworkMarker copy = marker;
server.getScheduler().scheduleSyncDelayedTask(library, new Runnable() {
@@ -786,22 +832,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
playerInjection.sendServerPacket(receiver, packet, marker, filters);
}
@Override
public void sendWirePacket(Player receiver, int id, byte[] bytes) throws InvocationTargetException {
WirePacket packet = new WirePacket(id, bytes);
sendWirePacket(receiver, packet);
}
@Override
public void sendWirePacket(Player receiver, WirePacket packet) throws InvocationTargetException {
Channel channel = playerInjection.getChannel(receiver);
if (channel == null) {
throw new InvocationTargetException(new NullPointerException(), "Failed to obtain channel for " + receiver.getName());
}
channel.writeAndFlush(packet);
}
@Override
public void recieveClientPacket(Player sender, PacketContainer packet) throws IllegalAccessException, InvocationTargetException {
recieveClientPacket(sender, packet, null, true);
@@ -958,7 +988,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
/**
* Register this protocol manager on Bukkit.
*
* @param manager - Bukkit plugin manager that provides player join/leave events.
* @param plugin - the parent plugin.
*/
@@ -969,34 +998,35 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
if (nettyInjector != null)
nettyInjector.inject();
manager.registerEvents(new Listener() {
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent event) {
try {
manager.registerEvents(new Listener() {
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent event) {
PacketFilterManager.this.onPlayerLogin(event);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPrePlayerJoin(PlayerJoinEvent event) {
@EventHandler(priority = EventPriority.LOWEST)
public void onPrePlayerJoin(PlayerJoinEvent event) {
PacketFilterManager.this.onPrePlayerJoin(event);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerJoin(PlayerJoinEvent event) {
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerJoin(PlayerJoinEvent event) {
PacketFilterManager.this.onPlayerJoin(event);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
PacketFilterManager.this.onPlayerQuit(event);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPluginDisabled(PluginDisableEvent event) {
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPluginDisabled(PluginDisableEvent event) {
PacketFilterManager.this.onPluginDisabled(event, plugin);
}
}
}, plugin);
}, plugin);
} catch (NoSuchMethodError e) {
// Oh wow! We're running on 1.0.0 or older.
registerOld(manager, plugin);
}
}
private void onPlayerLogin(PlayerLoginEvent event) {
@@ -1013,7 +1043,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
playerInjection.uninjectPlayer(event.getPlayer().getAddress());
playerInjection.injectPlayer(event.getPlayer(), ConflictStrategy.OVERRIDE);
} catch (ServerHandlerNull e) {
// Caused by logged out players, or fake login events in MCPC+/Cauldron. Ignore it.
// Caused by logged out players, or fake login events in MCPC++. Ignore it.
} catch (Exception e) {
reporter.reportDetailed(PacketFilterManager.this,
Report.newBuilder(REPORT_CANNOT_INJECT_PLAYER).callerParam(event).error(e)
@@ -1105,6 +1135,113 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
return PacketRegistry.getPacketClassFromID(packetID, forceVanilla);
}
// Yes, this is crazy.
@SuppressWarnings({ "unchecked", "rawtypes" })
private void registerOld(PluginManager manager, final Plugin plugin) {
try {
ClassLoader loader = manager.getClass().getClassLoader();
// The different enums we are going to need
Class eventTypes = loader.loadClass("org.bukkit.event.Event$Type");
Class eventPriority = loader.loadClass("org.bukkit.event.Event$Priority");
// Get the priority
Object priorityLowest = Enum.valueOf(eventPriority, "Lowest");
Object priorityMonitor = Enum.valueOf(eventPriority, "Monitor");
// Get event types
Object playerJoinType = Enum.valueOf(eventTypes, "PLAYER_JOIN");
Object playerQuitType = Enum.valueOf(eventTypes, "PLAYER_QUIT");
Object pluginDisabledType = Enum.valueOf(eventTypes, "PLUGIN_DISABLE");
// The player listener! Good times.
Class<?> playerListener = loader.loadClass("org.bukkit.event.player.PlayerListener");
Class<?> serverListener = loader.loadClass("org.bukkit.event.server.ServerListener");
// Find the register event method
Method registerEvent = FuzzyReflection.fromObject(manager).getMethodByParameters("registerEvent",
eventTypes, Listener.class, eventPriority, Plugin.class);
Enhancer playerLow = EnhancerFactory.getInstance().createEnhancer();
Enhancer playerEx = EnhancerFactory.getInstance().createEnhancer();
Enhancer serverEx = EnhancerFactory.getInstance().createEnhancer();
playerLow.setSuperclass(playerListener);
playerLow.setClassLoader(classLoader);
playerLow.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
// Must have a parameter
if (args.length == 1) {
Object event = args[0];
if (event instanceof PlayerJoinEvent) {
onPrePlayerJoin((PlayerJoinEvent) event);
}
}
return null;
}
});
playerEx.setSuperclass(playerListener);
playerEx.setClassLoader(classLoader);
playerEx.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (args.length == 1) {
Object event = args[0];
// Check for the correct event
if (event instanceof PlayerJoinEvent) {
onPlayerJoin((PlayerJoinEvent) event);
} else if (event instanceof PlayerQuitEvent) {
onPlayerQuit((PlayerQuitEvent) event);
}
}
return null;
}
});
serverEx.setSuperclass(serverListener);
serverEx.setClassLoader(classLoader);
serverEx.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// Must have a parameter
if (args.length == 1) {
Object event = args[0];
if (event instanceof PluginDisableEvent)
onPluginDisabled((PluginDisableEvent) event, plugin);
}
return null;
}
});
// Create our listener
Object playerProxyLow = playerLow.create();
Object playerProxy = playerEx.create();
Object serverProxy = serverEx.create();
registerEvent.invoke(manager, playerJoinType, playerProxyLow, priorityLowest, plugin);
registerEvent.invoke(manager, playerJoinType, playerProxy, priorityMonitor, plugin);
registerEvent.invoke(manager, playerQuitType, playerProxy, priorityMonitor, plugin);
registerEvent.invoke(manager, pluginDisabledType, serverProxy, priorityMonitor, plugin);
// A lot can go wrong
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* Retrieve every known and supported server packet.
* @return An immutable set of every known server packet.
@@ -4,7 +4,6 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginLoadOrder;
@@ -74,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;
}
@@ -100,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);
}
/**
@@ -160,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.
@@ -173,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;
}
@@ -184,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.
@@ -67,11 +67,9 @@ public class StructureCache {
// TODO: Optimize DefaultInstances
Object result = DefaultInstances.DEFAULT.create(clazz);
if (result == null) {
throw new IllegalArgumentException("Failed to create packet for type: " + type);
if (result != null) {
return result;
}
return result;
}
throw new IllegalArgumentException("Cannot find associated packet class: " + type);
}
@@ -174,4 +172,4 @@ public class StructureCache {
}
return result;
}
}
}
@@ -1,34 +1,19 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.netty;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.Callable;
import net.minecraft.util.io.netty.channel.Channel;
// Hopefully, CB won't version these as well
import net.minecraft.util.io.netty.channel.ChannelFuture;
import net.minecraft.util.io.netty.channel.ChannelHandler;
import com.google.common.collect.Lists;
public class BootstrapList implements List<Object> {
class BootstrapList implements List<Object> {
private List<Object> delegate;
private ChannelHandler handler;
@@ -131,102 +116,82 @@ public class BootstrapList implements List<Object> {
}
// Boiler plate
@Override
public synchronized int size() {
return delegate.size();
}
@Override
public synchronized boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean contains(Object o) {
return delegate.contains(o);
}
@Override
public synchronized Iterator<Object> iterator() {
return delegate.iterator();
}
@Override
public synchronized Object[] toArray() {
return delegate.toArray();
}
@Override
public synchronized <T> T[] toArray(T[] a) {
return delegate.toArray(a);
}
@Override
public synchronized boolean remove(Object o) {
return delegate.remove(o);
}
@Override
public synchronized boolean containsAll(Collection<?> c) {
return delegate.containsAll(c);
}
@Override
public synchronized boolean addAll(int index, Collection<? extends Object> c) {
return delegate.addAll(index, c);
}
@Override
public synchronized boolean removeAll(Collection<?> c) {
return delegate.removeAll(c);
}
@Override
public synchronized boolean retainAll(Collection<?> c) {
return delegate.retainAll(c);
}
@Override
public synchronized void clear() {
delegate.clear();
}
@Override
public synchronized Object get(int index) {
return delegate.get(index);
}
@Override
public synchronized void add(int index, Object element) {
delegate.add(index, element);
}
@Override
public synchronized Object remove(int index) {
return delegate.remove(index);
}
@Override
public synchronized int indexOf(Object o) {
return delegate.indexOf(o);
}
@Override
public synchronized int lastIndexOf(Object o) {
return delegate.lastIndexOf(o);
}
@Override
public synchronized ListIterator<Object> listIterator() {
return delegate.listIterator();
}
@Override
public synchronized ListIterator<Object> listIterator(int index) {
return delegate.listIterator(index);
}
@Override
public synchronized List<Object> subList(int fromIndex, int toIndex) {
return delegate.subList(fromIndex, toIndex);
}
@@ -1,31 +1,40 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.netty;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.*;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.ListIterator;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ConcurrentMap;
import net.minecraft.util.com.mojang.authlib.GameProfile;
import net.minecraft.util.io.netty.buffer.ByteBuf;
import net.minecraft.util.io.netty.channel.Channel;
import net.minecraft.util.io.netty.channel.ChannelHandler;
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
import net.minecraft.util.io.netty.channel.ChannelInboundHandler;
import net.minecraft.util.io.netty.channel.ChannelInboundHandlerAdapter;
import net.minecraft.util.io.netty.channel.ChannelPipeline;
import net.minecraft.util.io.netty.channel.ChannelPromise;
import net.minecraft.util.io.netty.channel.socket.SocketChannel;
import net.minecraft.util.io.netty.handler.codec.ByteToMessageDecoder;
import net.minecraft.util.io.netty.handler.codec.MessageToByteEncoder;
import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener;
import net.minecraft.util.io.netty.util.internal.TypeParameterMatcher;
import net.sf.cglib.proxy.Factory;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.annotations.Spigot;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide;
@@ -42,32 +51,18 @@ import com.comphenix.protocol.utility.MinecraftFields;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.AttributeKey;
import io.netty.util.internal.TypeParameterMatcher;
import net.sf.cglib.proxy.Factory;
import org.apache.commons.lang3.Validate;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import com.google.common.collect.MapMaker;
/**
* Represents a channel injector.
* @author Kristian
*/
public class ChannelInjector extends ByteToMessageDecoder implements Injector {
private static final ReportType REPORT_CANNOT_INTERCEPT_SERVER_PACKET = new ReportType("Unable to intercept a written server packet.");
private static final ReportType REPORT_CANNOT_INTERCEPT_CLIENT_PACKET = new ReportType("Unable to intercept a read client packet.");
private static final ReportType REPORT_CANNOT_EXECUTE_IN_CHANNEL_THREAD = new ReportType("Cannot execute code in channel thread.");
private static final ReportType REPORT_CANNOT_SEND_PACKET = new ReportType("Unable to send packet %s to %s");
class ChannelInjector extends ByteToMessageDecoder implements Injector {
public static final ReportType REPORT_CANNOT_INTERCEPT_SERVER_PACKET = new ReportType("Unable to intercept a written server packet.");
public static final ReportType REPORT_CANNOT_INTERCEPT_CLIENT_PACKET = new ReportType("Unable to intercept a read client packet.");
public static final ReportType REPORT_CANNOT_EXECUTE_IN_CHANNEL_THREAD = new ReportType("Cannot execute code in channel thread.");
public static final ReportType REPORT_CANNOT_FIND_GET_VERSION = new ReportType("Cannot find getVersion() in NetworkMananger");
/**
* Indicates that a packet has bypassed packet listeners.
@@ -78,16 +73,6 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
private static Class<?> PACKET_LOGIN_CLIENT = null;
private static FieldAccessor LOGIN_GAME_PROFILE = null;
// Versioning
private static Class<?> PACKET_SET_PROTOCOL = null;
private static AtomicInteger keyId = new AtomicInteger();
private static AttributeKey<Integer> PROTOCOL_KEY;
static {
PROTOCOL_KEY = AttributeKey.valueOf("PROTOCOL-" + keyId.getAndIncrement());
}
// Saved accessors
private static MethodAccessor DECODE_BUFFER;
private static MethodAccessor ENCODE_BUFFER;
@@ -96,13 +81,15 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// For retrieving the protocol
private static FieldAccessor PROTOCOL_ACCESSOR;
// Current version
private static volatile MethodAccessor PROTOCOL_VERSION;
// The factory that created this injector
private InjectionFactory factory;
// The player, or temporary player
private Player player;
private Player updated;
private String playerName;
// The player connection
private Object playerConnection;
@@ -113,7 +100,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
private VolatileField channelField;
// Known network markers
private Map<Object, NetworkMarker> packetMarker = new WeakHashMap<>();
private ConcurrentMap<Object, NetworkMarker> packetMarker = new MapMaker().weakKeys().makeMap();
/**
* Indicate that this packet has been processed by event listeners.
@@ -130,13 +117,21 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
/**
* A flag set by the main thread to indiciate that a packet should not be processed.
*/
private final ThreadLocal<Boolean> scheduleProcessPackets = ThreadLocal.withInitial(() -> true);
private final ThreadLocal<Boolean> scheduleProcessPackets = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return true;
};
};
// Other handlers
private ByteToMessageDecoder vanillaDecoder;
private MessageToByteEncoder<Object> vanillaEncoder;
private Deque<PacketEvent> finishQueue = new ArrayDeque<>();
// Our extra handlers
private MessageToByteEncoder<Object> protocolEncoder;
private ChannelInboundHandler finishHandler;
private Deque<PacketEvent> finishQueue = new ArrayDeque<PacketEvent>();
// The channel listener
private ChannelListener channelListener;
@@ -156,7 +151,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param channelListener - a listener.
* @param factory - the factory that created this injector
*/
ChannelInjector(Player player, Object networkManager, Channel channel, ChannelListener channelListener, InjectionFactory factory) {
public ChannelInjector(Player player, Object networkManager, Channel channel, ChannelListener channelListener, InjectionFactory factory) {
this.player = Preconditions.checkNotNull(player, "player cannot be NULL");
this.networkManager = Preconditions.checkNotNull(networkManager, "networkMananger cannot be NULL");
this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL");
@@ -165,19 +160,37 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
this.processor = new NetworkProcessor(ProtocolLibrary.getErrorReporter());
// Get the channel field
this.channelField = new VolatileField(FuzzyReflection
.fromObject(networkManager, true)
.getFieldByType("channel", Channel.class), networkManager, true);
this.channelField = new VolatileField(
FuzzyReflection.fromObject(networkManager, true).
getFieldByType("channel", Channel.class),
networkManager, true);
}
/**
* Get the version of the current protocol.
* @return The version.
*/
@Spigot(minimumBuild = 1628)
@Override
public int getProtocolVersion() {
Integer value = originalChannel.attr(PROTOCOL_KEY).get();
return value != null ? value : MinecraftProtocolVersion.getCurrentVersion();
MethodAccessor accessor = PROTOCOL_VERSION;
if (accessor == null) {
try {
accessor = Accessors.getMethodAccessor(networkManager.getClass(), "getVersion");
} catch (RuntimeException e) {
// Notify user
ProtocolLibrary.getErrorReporter().reportWarning(
this, Report.newBuilder(REPORT_CANNOT_FIND_GET_VERSION).error(e));
// Fallback method
accessor = Accessors.getConstantAccessor(
MinecraftProtocolVersion.getCurrentVersion(), null);
}
PROTOCOL_VERSION = accessor;
}
return (Integer) accessor.invoke(networkManager);
}
@Override
@@ -195,7 +208,12 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// pipeline with only some of the handlers removed
if (Bukkit.isPrimaryThread()) {
// Just like in the close() method, we'll avoid blocking the main thread
executeInChannelThread(this::inject);
executeInChannelThread(new Runnable() {
@Override
public void run() {
inject();
}
});
return false; // We don't know
}
@@ -209,9 +227,9 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
vanillaEncoder = (MessageToByteEncoder<Object>) originalChannel.pipeline().get("encoder");
if (vanillaDecoder == null)
throw new IllegalArgumentException("Unable to find vanilla decoder in " + originalChannel.pipeline());
throw new IllegalArgumentException("Unable to find vanilla decoder in " + originalChannel.pipeline() );
if (vanillaEncoder == null)
throw new IllegalArgumentException("Unable to find vanilla encoder in " + originalChannel.pipeline());
throw new IllegalArgumentException("Unable to find vanilla encoder in " + originalChannel.pipeline() );
patchEncoder(vanillaEncoder);
if (DECODE_BUFFER == null)
@@ -222,31 +240,26 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
"encode", ChannelHandlerContext.class, Object.class, ByteBuf.class);
// Intercept sent packets
MessageToByteEncoder<Object> protocolEncoder = new MessageToByteEncoder<Object>() {
protocolEncoder = new MessageToByteEncoder<Object>() {
@Override
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) {
if (packet instanceof WirePacket) {
// Special case for wire format
ChannelInjector.this.encodeWirePacket((WirePacket) packet, output);
} else {
ChannelInjector.this.encode(ctx, packet, output);
}
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
ChannelInjector.this.encode(ctx, packet, output);
}
@Override
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
super.write(ctx, packet, promise);
ChannelInjector.this.finalWrite();
ChannelInjector.this.finalWrite(ctx, packet, promise);
}
};
// Intercept recieved packets
ChannelInboundHandlerAdapter finishHandler = new ChannelInboundHandlerAdapter() {
finishHandler = new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// Execute context first
ctx.fireChannelRead(msg);
ChannelInjector.this.finishRead();
ChannelInjector.this.finishRead(ctx, msg);
}
};
@@ -257,18 +270,17 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// Intercept all write methods
channelField.setValue(new ChannelProxy(originalChannel, MinecraftReflection.getPacketClass()) {
// Compatibility with Spigot 1.8
// Compatibility with Spigot 1.8 protocol hack
private final PipelineProxy pipelineProxy = new PipelineProxy(originalChannel.pipeline(), this) {
@Override
public ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler) {
// Correct the position of the decoder
if ("decoder".equals(baseName)) {
if (super.get("protocol_lib_decoder") != null && guessCompression(handler)) {
if (super.get("protocol_lib_decoder") != null && guessSpigotHandler(handler)) {
super.addBefore("protocol_lib_decoder", name, handler);
return this;
}
}
return super.addBefore(baseName, name, handler);
}
};
@@ -286,14 +298,17 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
if (event != null && event.isCancelled())
return null;
return () -> {
T result;
return new Callable<T>() {
@Override
public T call() throws Exception {
T result = null;
// This field must only be updated in the pipeline thread
currentEvent = event;
result = callable.call();
currentEvent = null;
return result;
// This field must only be updated in the pipeline thread
currentEvent = event;
result = callable.call();
currentEvent = null;
return result;
}
};
}
@@ -305,14 +320,17 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
if (event != null && event.isCancelled())
return null;
return () -> {
currentEvent = event;
runnable.run();
currentEvent = null;
return new Runnable() {
@Override
public void run() {
currentEvent = event;
runnable.run();
currentEvent = null;
}
};
}
PacketEvent handleScheduled(Object instance, FieldAccessor accessor) {
protected PacketEvent handleScheduled(Object instance, FieldAccessor accessor) {
// Let the filters handle this packet
Object original = accessor.get(instance);
@@ -328,17 +346,15 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
return BYPASSED_PACKET;
}
}
PacketEvent event = processSending(original);
if (event != null && !event.isCancelled()) {
Object changed = event.getPacket().getHandle();
// Change packet to be scheduled
if (original != changed) {
if (original != changed)
accessor.set(instance, changed);
}
}
};
return event != null ? event : BYPASSED_PACKET;
}
});
@@ -349,13 +365,15 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
}
/**
* Determine if the given object is a compressor or decompressor.
* Determine if the given object is a Spigot channel handler.
* @param handler - object to test.
* @return TRUE if it is, FALSE if not or unknown.
*/
private boolean guessCompression(ChannelHandler handler) {
String className = handler != null ? handler.getClass().getCanonicalName() : "";
return className.contains("Compressor") || className.contains("Decompressor");
private boolean guessSpigotHandler(ChannelHandler handler) {
String className = handler != null ? handler.getClass().getCanonicalName() : null;
return "org.spigotmc.SpigotDecompressor".equals(className) ||
"org.spigotmc.SpigotCompressor".equals(className);
}
/**
@@ -375,31 +393,24 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
if (ENCODER_TYPE_MATCHER == null) {
ENCODER_TYPE_MATCHER = Accessors.getFieldAccessor(encoder.getClass(), "matcher", true);
}
ENCODER_TYPE_MATCHER.set(encoder, TypeParameterMatcher.get(MinecraftReflection.getPacketClass()));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (channelListener.isDebug()) {
if (channelListener.isDebug())
cause.printStackTrace();
}
super.exceptionCaught(ctx, cause);
}
private void encodeWirePacket(WirePacket packet, ByteBuf output) {
packet.writeId(output);
packet.writeBytes(output);
}
/**
* Encode a packet to a byte buffer, taking over for the standard Minecraft encoder.
* @param ctx - the current context.
* @param packet - the packet to encode to a byte array.
* @param output - the output byte array.
* @throws Exception If anything went wrong.
*/
private void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) {
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
NetworkMarker marker = null;
PacketEvent event = currentEvent;
@@ -428,7 +439,6 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
}
}
}
if (event != null) {
// Retrieve marker without accidentally constructing it
marker = NetworkMarker.getNetworkMarker(event);
@@ -448,6 +458,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// Sent listeners?
finalEvent = event;
return;
}
} catch (Exception e) {
channelListener.getReporter().reportDetailed(this,
@@ -462,9 +473,12 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
}
/**
* Invoked when a packet has been written to the channel
* Invoked when a packet has been written to the channel.
* @param ctx - current context.
* @param packet - the packet that has been written.
* @param promise - a promise.
*/
private void finalWrite() {
protected void finalWrite(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) {
PacketEvent event = finalEvent;
if (event != null) {
@@ -478,15 +492,17 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
private void scheduleMainThread(final Object packetCopy) {
// Don't use BukkitExecutors for this - it has a bit of overhead
Bukkit.getScheduler().scheduleSyncDelayedTask(factory.getPlugin(), () -> {
if (!closed) {
Bukkit.getScheduler().scheduleSyncDelayedTask(factory.getPlugin(), new Runnable() {
@Override
public void run() {
invokeSendPacket(packetCopy);
}
});
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List<Object> packets) {
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List<Object> packets) throws Exception {
byteBuffer.markReaderIndex();
DECODE_BUFFER.invoke(vanillaDecoder, ctx, byteBuffer, packets);
try {
@@ -505,20 +521,18 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
byteBuffer.resetReaderIndex();
marker = new NettyNetworkMarker(ConnectionSide.CLIENT_SIDE, getBytes(byteBuffer));
}
PacketEvent output = channelListener.onPacketReceiving(this, input, marker);
// Handle packet changes
if (output != null) {
if (output.isCancelled()) {
it.remove();
} else {
if (output.getPacket().getHandle() != input) {
it.set(output.getPacket().getHandle());
}
finishQueue.addLast(output);
continue;
} else if (output.getPacket().getHandle() != input) {
it.set(output.getPacket().getHandle());
}
finishQueue.addLast(output);
}
}
} catch (Exception e) {
@@ -528,9 +542,11 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
}
/**
* Invoked after our decoder
* Invoked after our decoder.
* @param ctx - current context.
* @param msg - the current packet.
*/
private void finishRead() {
protected void finishRead(ChannelHandlerContext ctx, Object msg) {
// Assume same order
PacketEvent event = finishQueue.pollFirst();
@@ -548,46 +564,27 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param packetClass - the packet class.
* @param packet - the packet.
*/
private void handleLogin(Class<?> packetClass, Object packet) {
// Try to find the login packet class
if (PACKET_LOGIN_CLIENT == null) {
PACKET_LOGIN_CLIENT = PacketType.Login.Client.START.getPacketClass();
}
protected void handleLogin(Class<?> packetClass, Object packet) {
Class<?> loginClass = PACKET_LOGIN_CLIENT;
FieldAccessor loginClient = LOGIN_GAME_PROFILE;
// If we can't, there's an issue
if (PACKET_LOGIN_CLIENT == null) {
throw new IllegalStateException("Failed to obtain login start packet. Did you build Spigot with BuildTools?");
// Initialize packet class and login
if (loginClass == null) {
loginClass = PacketType.Login.Client.START.getPacketClass();
PACKET_LOGIN_CLIENT = loginClass;
}
if (LOGIN_GAME_PROFILE == null) {
LOGIN_GAME_PROFILE = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, MinecraftReflection.getGameProfileClass(), true);
if (loginClient == null) {
loginClient = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, GameProfile.class, true);
LOGIN_GAME_PROFILE = loginClient;
}
// See if we are dealing with the login packet
if (PACKET_LOGIN_CLIENT.equals(packetClass)) {
WrappedGameProfile profile = WrappedGameProfile.fromHandle(LOGIN_GAME_PROFILE.get(packet));
if (loginClass.equals(packetClass)) {
GameProfile profile = (GameProfile) loginClient.get(packet);
// Save the channel injector
factory.cacheInjector(profile.getName(), this);
}
if (PACKET_SET_PROTOCOL == null) {
try {
PACKET_SET_PROTOCOL = PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass();
} catch (Throwable ex) {
PACKET_SET_PROTOCOL = getClass(); // If we can't find it don't worry about it
}
}
if (PACKET_SET_PROTOCOL.equals(packetClass)) {
FuzzyReflection fuzzy = FuzzyReflection.fromObject(packet);
try {
int protocol = (int) fuzzy.invokeMethod(packet, "getProtocol", int.class);
originalChannel.attr(PROTOCOL_KEY).set(protocol);
} catch (Throwable ex) {
// Oh well
}
}
}
@Override
@@ -605,7 +602,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param buffer - the buffer.
* @return The bytes.
*/
private byte[] getBytes(ByteBuf buffer) {
private byte[] getBytes(ByteBuf buffer){
byte[] data = new byte[buffer.readableBytes()];
buffer.readBytes(data);
@@ -648,18 +645,15 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param packet - the packet to send.
*/
private void invokeSendPacket(Object packet) {
Validate.isTrue(!closed, "cannot send packets to a closed channel");
// Attempt to send the packet with NetworkMarker.handle(), or the PlayerConnection if its active
try {
if (player instanceof Factory) {
MinecraftMethods.getNetworkManagerHandleMethod().invoke(networkManager, packet);
MinecraftMethods.getNetworkManagerHandleMethod().invoke(networkManager, packet, new GenericFutureListener[0]);
} else {
MinecraftMethods.getSendPacketMethod().invoke(getPlayerConnection(), packet);
}
} catch (Throwable ex) {
ProtocolLibrary.getErrorReporter().reportWarning(this,
Report.newBuilder(REPORT_CANNOT_SEND_PACKET).messageParam(packet, playerName).error(ex).build());
} catch (Exception e) {
throw new RuntimeException("Unable to send server packet " + packet, e);
}
}
@@ -668,12 +662,15 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// TODO: Ensure the packet listeners are executed in the channel thread.
// Execute this in the channel thread
Runnable action = () -> {
try {
MinecraftMethods.getNetworkManagerReadPacketMethod().invoke(networkManager, null, packet);
} catch (Exception e) {
// Inform the user
ProtocolLibrary.getErrorReporter().reportMinimal(factory.getPlugin(), "recieveClientPacket", e);
Runnable action = new Runnable() {
@Override
public void run() {
try {
MinecraftMethods.getNetworkManagerReadPacketMethod().invoke(networkManager, null, packet);
} catch (Exception e) {
// Inform the user
ProtocolLibrary.getErrorReporter().reportMinimal(factory.getPlugin(), "recieveClientPacket", e);
}
}
};
@@ -700,14 +697,8 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
*/
private Object getPlayerConnection() {
if (playerConnection == null) {
Player player = getPlayer();
if (player == null) {
throw new IllegalStateException("cannot send packet to offline player" + (playerName != null ? " " + playerName : ""));
}
playerConnection = MinecraftFields.getPlayerConnection(player);
}
return playerConnection;
}
@@ -725,10 +716,6 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
@Override
public Player getPlayer() {
if (player == null && playerName != null) {
return Bukkit.getPlayerExact(playerName);
}
return player;
}
@@ -739,7 +726,6 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
@Override
public void setPlayer(Player player) {
this.player = player;
this.playerName = player.getName();
}
/**
@@ -749,7 +735,6 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
@Override
public void setUpdatedPlayer(Player updated) {
this.updated = updated;
this.playerName = updated.getName();
}
@Override
@@ -790,30 +775,29 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// we end up with a deadlock. The main thread is waiting for the worker thread to process the task, and
// the worker thread is waiting for the main thread to finish executing PlayerQuitEvent.
//
// TL;DR: Concurrency is hard.
executeInChannelThread(() -> {
String[] handlers = new String[] {
"protocol_lib_decoder", "protocol_lib_finish", "protocol_lib_encoder"
};
for (String handler : handlers) {
try {
originalChannel.pipeline().remove(handler);
} catch (NoSuchElementException e) {
// Ignore
// TLDR: Concurrenty is hard.
executeInChannelThread(new Runnable() {
@Override
public void run() {
for (ChannelHandler handler : new ChannelHandler[] { ChannelInjector.this, finishHandler, protocolEncoder }) {
try {
originalChannel.pipeline().remove(handler);
} catch (NoSuchElementException e) {
// Ignore
}
}
}
});
// Clear cache
factory.invalidate(player);
// Clear player instances
// Should help fix memory leaks
this.player = null;
this.updated = null;
}
}
// dmulloy2 - clear player instances
// Should fix memory leaks
this.player = null;
this.updated = null;
}
/**
@@ -823,12 +807,15 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param command - the command to execute.
*/
private void executeInChannelThread(final Runnable command) {
originalChannel.eventLoop().execute(() -> {
try {
command.run();
} catch (Exception e) {
ProtocolLibrary.getErrorReporter().reportDetailed(ChannelInjector.this,
originalChannel.eventLoop().execute(new Runnable() {
@Override
public void run() {
try {
command.run();
} catch (Exception e) {
ProtocolLibrary.getErrorReporter().reportDetailed(ChannelInjector.this,
Report.newBuilder(REPORT_CANNOT_EXECUTE_IN_CHANNEL_THREAD).error(e).build());
}
}
});
}
@@ -839,7 +826,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param clazz - the type.
* @return The first handler, or NULL.
*/
static ChannelHandler findChannelHandler(Channel channel, Class<?> clazz) {
public static ChannelHandler findChannelHandler(Channel channel, Class<?> clazz) {
for (Entry<String, ChannelHandler> entry : channel.pipeline()) {
if (clazz.isAssignableFrom(entry.getValue().getClass())) {
return entry.getValue();
@@ -852,41 +839,41 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* Represents a socket injector that foreards to the current channel injector.
* @author Kristian
*/
public static class ChannelSocketInjector implements SocketInjector {
static class ChannelSocketInjector implements SocketInjector {
private final ChannelInjector injector;
ChannelSocketInjector(ChannelInjector injector) {
public ChannelSocketInjector(ChannelInjector injector) {
this.injector = Preconditions.checkNotNull(injector, "injector cannot be NULL");
}
@Override
public Socket getSocket() {
return SocketAdapter.adapt((SocketChannel) injector.originalChannel);
public Socket getSocket() throws IllegalAccessException {
return NettySocketAdaptor.adapt((SocketChannel) injector.originalChannel);
}
@Override
public SocketAddress getAddress() {
public SocketAddress getAddress() throws IllegalAccessException {
return injector.originalChannel.remoteAddress();
}
@Override
public void disconnect(String message) {
public void disconnect(String message) throws InvocationTargetException {
injector.disconnect(message);
}
@Override
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException {
injector.sendServerPacket(packet, marker, filtered);
}
@Override
public Player getPlayer() {
return injector.getPlayer();
return injector.player;
}
@Override
public Player getUpdatedPlayer() {
return injector.updated != null ? injector.updated : getPlayer();
return injector.updated;
}
@Override
@@ -896,15 +883,11 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
@Override
public void setUpdatedPlayer(Player updatedPlayer) {
injector.setPlayer(updatedPlayer);
injector.player = updatedPlayer;
}
ChannelInjector getChannelInjector() {
public ChannelInjector getChannelInjector() {
return injector;
}
}
public Channel getChannel() {
return originalChannel;
}
}
}
@@ -4,11 +4,12 @@ import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketEvent;
/**
* Represents a listener for received or sent packets.
* @author Kristian
*/
public interface ChannelListener {
interface ChannelListener {
/**
* Invoked when a packet is being sent to the client.
* <p>
@@ -63,4 +64,4 @@ public interface ChannelListener {
* @return TRUE if it is, FALSE otherwise.
*/
boolean isDebug();
}
}
@@ -1,19 +1,3 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.netty;
import java.lang.reflect.Field;
@@ -21,23 +5,23 @@ import java.net.SocketAddress;
import java.util.Map;
import java.util.concurrent.Callable;
import net.minecraft.util.io.netty.buffer.ByteBufAllocator;
import net.minecraft.util.io.netty.channel.Channel;
import net.minecraft.util.io.netty.channel.ChannelConfig;
import net.minecraft.util.io.netty.channel.ChannelFuture;
import net.minecraft.util.io.netty.channel.ChannelMetadata;
import net.minecraft.util.io.netty.channel.ChannelPipeline;
import net.minecraft.util.io.netty.channel.ChannelProgressivePromise;
import net.minecraft.util.io.netty.channel.ChannelPromise;
import net.minecraft.util.io.netty.channel.EventLoop;
import net.minecraft.util.io.netty.util.Attribute;
import net.minecraft.util.io.netty.util.AttributeKey;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.google.common.collect.Maps;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelProgressivePromise;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
public abstract class ChannelProxy implements Channel {
abstract class ChannelProxy implements Channel {
// Mark that a certain object does not contain a message field
private static final FieldAccessor MARK_NO_MESSAGE = new FieldAccessor() {
@Override
@@ -65,7 +49,6 @@ public abstract class ChannelProxy implements Channel {
/**
* Invoked when a packet is scheduled for transmission in the event loop.
* @param <T> Type
* @param callable - callable to schedule for execution.
* @param packetAccessor - accessor for modifying the packet in the callable.
* @return The callable that will be scheduled, or NULL to cancel.
@@ -331,22 +314,4 @@ public abstract class ChannelProxy implements Channel {
public int compareTo(Channel o) {
return delegate.compareTo(o);
}
/* Added in Netty 4.1, seem to be unused
public long bytesBeforeUnwritable() {
return delegate.bytesBeforeUnwritable();
}
public long bytesBeforeWritable() {
return delegate.bytesBeforeWritable();
}
public ChannelId id() {
return delegate.id();
}
public <T> boolean hasAttr(AttributeKey<T> key) {
return delegate.hasAttr(key);
}
*/
}
@@ -9,7 +9,7 @@ import com.comphenix.protocol.events.NetworkMarker;
* Represents a closed injector.
* @author Kristian
*/
public class ClosedInjector implements Injector {
class ClosedInjector implements Injector {
private Player player;
/**
Loaded 100 of 363 files, more files were not shown because too many files have changed in this diff. Show more