Compare commits

...
Author SHA1 Message Date
Dan Mulloy d9c623a254 backwards compat 2022-12-13 20:32:53 -05:00
Dan Mulloy fe0abbf9b3 revert some changes to MinecraftReflection 2022-12-13 20:30:51 -05:00
Dan Mulloy 92b604506f line endings 2022-12-13 20:25:07 -05:00
derklaro 574c34ad5c remove unneeded packet class names 2022-12-08 09:33:29 +01:00
derklaro 115cc83040 resolve conflicts 2022-12-08 09:14:04 +01:00
derklaro a18c418f42 1.19.3 2022-12-08 09:03:45 +01:00
Dan Mulloy 6aaf0ec26b Normalize line endings to LF 2022-12-07 13:52:09 -05:00
derklaro 5ef24a7471 22w43a 2022-10-27 16:04:17 +02:00
derklaro 475f89ab89 deprecate WrappedServerPing chat preview stuff 2022-10-20 10:10:41 +02:00
derklaro e3d4339659 player info action changes 2022-10-19 20:04:33 +02:00
derklaro e7d54cdabd update to 1.19.3 2022-10-19 16:24:10 +02:00
51 changed files with 6796 additions and 6425 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
* text=auto eol=lf
+167 -167
View File
@@ -1,167 +1,167 @@
# ProtocolLib
Certain tasks are impossible to perform with the standard Bukkit API, and may require
working with and even modifying Minecraft directly. A common technique is to modify incoming
and outgoing [packets](https://www.wiki.vg/Protocol), or inject custom packets into the
stream. This is quite cumbersome to do, however, and most implementations will break
as soon as a new version of Minecraft has been released, mostly due to obfuscation.
Critically, different plugins that use this approach may _hook_ into the same classes,
with unpredictable outcomes. More than often this causes plugins to crash, but it may also
lead to more subtle bugs.
Currently maintained by dmulloy2 on behalf of [Spigot](https://www.spigotmc.org/).
### Resources
* [Resource Page](https://www.spigotmc.org/resources/protocollib.1997/)
* [Dev Builds](https://ci.dmulloy2.net/job/ProtocolLib)
* [JavaDoc](https://ci.dmulloy2.net/job/ProtocolLib/javadoc/index.html)
### Compilation
ProtocolLib is built with [Maven](https://maven.apache.org/). If you have it installed, just run
`mvn package` in the root project folder.
### A new API
__ProtocolLib__ attempts to solve this problem by providing an event API, much like Bukkit,
that allows plugins to monitor, modify, or cancel packets sent and received. But, more importantly,
the API also hides all the gritty, obfuscated classes with a simple index based read/write system.
You no longer have to reference CraftBukkit!
### Using ProtocolLib
To use this library, first add ProtocolLib.jar to your Java build path. Then, add ProtocolLib
as a dependency or soft dependency to your plugin.yml file like any other plugin:
````yml
depend: [ ProtocolLib ]
````
You can also add ProtocolLib as a Maven dependency:
````xml
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>4.7.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
````
Or use the maven dependency with gradle:
```gradle
repositories {
maven { url "https://repo.dmulloy2.net/repository/public/" }
}
dependencies {
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.7.0";
}
```
Then get a reference to ProtocolManager in onLoad() or onEnable() and you're good to go.
````java
private ProtocolManager protocolManager;
public void onLoad() {
protocolManager = ProtocolLibrary.getProtocolManager();
}
````
To listen for packets sent by the server to a client, add a server-side listener:
````java
// Disable all sound effects
protocolManager.addPacketListener(new PacketAdapter(
this,
ListenerPriority.NORMAL,
PacketType.Play.Server.NAMED_SOUND_EFFECT
) {
@Override
public void onPacketSending(PacketEvent event) {
event.setCancelled(true);
}
});
````
It's also possible to read and modify the content of these packets. For instance, you can create a global
censor by listening for Packet3Chat events:
````java
// Censor
protocolManager.addPacketListener(new PacketAdapter(
this,
ListenerPriority.NORMAL,
PacketType.Play.Client.CHAT
) {
@Override
public void onPacketReceiving(PacketEvent event) {
PacketContainer packet = event.getPacket();
String message = packet.getStrings().read(0);
if (message.contains("shit") || message.contains("damn")) {
event.setCancelled(true);
event.getPlayer().sendMessage("Bad manners!");
}
}
});
````
### Sending packets
Normally, you might have to do something ugly like the following:
````java
PacketPlayOutExplosion fakeExplosion = new PacketPlayOutExplosion(
player.getLocation().getX(),
player.getLocation().getY(),
player.getLocation().getZ(),
3.0F,
new ArrayList<>(),
new Vec3D(
player.getVelocity().getX() + 1,
player.getVelocity().getY() + 1,
player.getVelocity().getZ() + 1
)
);
((CraftPlayer) player).getHandle().b.a(fakeExplosion);
````
But with ProtocolLib, you can turn that into something more manageable:
````java
PacketContainer fakeExplosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
fakeExplosion.getDoubles()
.write(0, player.getLocation().getX())
.write(1, player.getLocation().getY())
.write(2, player.getLocation().getZ());
fakeExplosion.getFloat().write(0, 3.0F);
fakeExplosion.getBlockPositionCollectionModifier().write(0, new ArrayList<>());
fakeExplosion.getVectors().write(0, player.getVelocity().add(new Vector(1, 1, 1)));
protocolManager.sendServerPacket(player, fakeExplosion);
````
### Compatibility
One of the main goals of this project was to achieve maximum compatibility with CraftBukkit. And the end
result is quite flexible. It's likely that I won't have to update ProtocolLib for anything but bug fixes
and new features.
How is this possible? It all comes down to reflection in the end. Essentially, no name is hard coded -
every field, method and class is deduced by looking at field types, package names or parameter
types. It's remarkably consistent across different versions.
# ProtocolLib
Certain tasks are impossible to perform with the standard Bukkit API, and may require
working with and even modifying Minecraft directly. A common technique is to modify incoming
and outgoing [packets](https://www.wiki.vg/Protocol), or inject custom packets into the
stream. This is quite cumbersome to do, however, and most implementations will break
as soon as a new version of Minecraft has been released, mostly due to obfuscation.
Critically, different plugins that use this approach may _hook_ into the same classes,
with unpredictable outcomes. More than often this causes plugins to crash, but it may also
lead to more subtle bugs.
Currently maintained by dmulloy2 on behalf of [Spigot](https://www.spigotmc.org/).
### Resources
* [Resource Page](https://www.spigotmc.org/resources/protocollib.1997/)
* [Dev Builds](https://ci.dmulloy2.net/job/ProtocolLib)
* [JavaDoc](https://ci.dmulloy2.net/job/ProtocolLib/javadoc/index.html)
### Compilation
ProtocolLib is built with [Maven](https://maven.apache.org/). If you have it installed, just run
`mvn package` in the root project folder.
### A new API
__ProtocolLib__ attempts to solve this problem by providing an event API, much like Bukkit,
that allows plugins to monitor, modify, or cancel packets sent and received. But, more importantly,
the API also hides all the gritty, obfuscated classes with a simple index based read/write system.
You no longer have to reference CraftBukkit!
### Using ProtocolLib
To use this library, first add ProtocolLib.jar to your Java build path. Then, add ProtocolLib
as a dependency or soft dependency to your plugin.yml file like any other plugin:
````yml
depend: [ ProtocolLib ]
````
You can also add ProtocolLib as a Maven dependency:
````xml
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>4.7.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
````
Or use the maven dependency with gradle:
```gradle
repositories {
maven { url "https://repo.dmulloy2.net/repository/public/" }
}
dependencies {
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.7.0";
}
```
Then get a reference to ProtocolManager in onLoad() or onEnable() and you're good to go.
````java
private ProtocolManager protocolManager;
public void onLoad() {
protocolManager = ProtocolLibrary.getProtocolManager();
}
````
To listen for packets sent by the server to a client, add a server-side listener:
````java
// Disable all sound effects
protocolManager.addPacketListener(new PacketAdapter(
this,
ListenerPriority.NORMAL,
PacketType.Play.Server.NAMED_SOUND_EFFECT
) {
@Override
public void onPacketSending(PacketEvent event) {
event.setCancelled(true);
}
});
````
It's also possible to read and modify the content of these packets. For instance, you can create a global
censor by listening for Packet3Chat events:
````java
// Censor
protocolManager.addPacketListener(new PacketAdapter(
this,
ListenerPriority.NORMAL,
PacketType.Play.Client.CHAT
) {
@Override
public void onPacketReceiving(PacketEvent event) {
PacketContainer packet = event.getPacket();
String message = packet.getStrings().read(0);
if (message.contains("shit") || message.contains("damn")) {
event.setCancelled(true);
event.getPlayer().sendMessage("Bad manners!");
}
}
});
````
### Sending packets
Normally, you might have to do something ugly like the following:
````java
PacketPlayOutExplosion fakeExplosion = new PacketPlayOutExplosion(
player.getLocation().getX(),
player.getLocation().getY(),
player.getLocation().getZ(),
3.0F,
new ArrayList<>(),
new Vec3D(
player.getVelocity().getX() + 1,
player.getVelocity().getY() + 1,
player.getVelocity().getZ() + 1
)
);
((CraftPlayer) player).getHandle().b.a(fakeExplosion);
````
But with ProtocolLib, you can turn that into something more manageable:
````java
PacketContainer fakeExplosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
fakeExplosion.getDoubles()
.write(0, player.getLocation().getX())
.write(1, player.getLocation().getY())
.write(2, player.getLocation().getZ());
fakeExplosion.getFloat().write(0, 3.0F);
fakeExplosion.getBlockPositionCollectionModifier().write(0, new ArrayList<>());
fakeExplosion.getVectors().write(0, player.getVelocity().add(new Vector(1, 1, 1)));
protocolManager.sendServerPacket(player, fakeExplosion);
````
### Compatibility
One of the main goals of this project was to achieve maximum compatibility with CraftBukkit. And the end
result is quite flexible. It's likely that I won't have to update ProtocolLib for anything but bug fixes
and new features.
How is this possible? It all comes down to reflection in the end. Essentially, no name is hard coded -
every field, method and class is deduced by looking at field types, package names or parameter
types. It's remarkably consistent across different versions.
+1 -1
View File
@@ -19,7 +19,7 @@
<junit.version>5.9.0</junit.version>
<mockito.version>4.6.1</mockito.version>
<netty.version>4.1.77.Final</netty.version>
<spigot.version>1.19.2-R0.1-SNAPSHOT</spigot.version>
<spigot.version>1.19.3-R0.1-SNAPSHOT</spigot.version>
</properties>
<build>
@@ -1,284 +1,284 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 Dan Mulloy
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* Logs packets to a given stream
* @author dmulloy2
*/
public class PacketLogging implements CommandExecutor, PacketListener {
public static final String NAME = "packetlog";
private static MethodAccessor HEX_DUMP;
private List<PacketType> sendingTypes = new ArrayList<>();
private List<PacketType> receivingTypes = new ArrayList<>();
private ListeningWhitelist sendingWhitelist;
private ListeningWhitelist receivingWhitelist;
private Logger fileLogger;
private LogLocation location = LogLocation.FILE;
private final ProtocolManager manager;
private final Plugin plugin;
PacketLogging(Plugin plugin, ProtocolManager manager) {
this.plugin = plugin;
this.manager = manager;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
PacketType type = null;
try {
if (args.length > 2) {
Protocol protocol;
try {
protocol = Protocol.valueOf(args[0].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown protocol " + args[0]);
return true;
}
Sender pSender;
try {
pSender = Sender.valueOf(args[1].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown sender: " + args[1]);
return true;
}
try {
try { // Try IDs first
int id = Integer.parseInt(args[2]);
type = PacketType.findCurrent(protocol, pSender, id);
} catch (NumberFormatException ex) { // Check packet names
String name = args[2];
for (PacketType packet : PacketType.values()) {
if (packet.getProtocol() == protocol && packet.getSender() == pSender) {
if (packet.name().equalsIgnoreCase(name)) {
type = packet;
break;
}
for (String className : packet.getClassNames()) {
if (className.equalsIgnoreCase(name)) {
type = packet;
break;
}
}
}
}
}
} catch (IllegalArgumentException ex) { // RIP
type = null;
}
if (type == null) {
sender.sendMessage(ChatColor.RED + "Unknown packet: " + args[2]);
return true;
}
if (args.length > 3) {
if (args[3].equalsIgnoreCase("console")) {
this.location = LogLocation.CONSOLE;
} else {
this.location = LogLocation.FILE;
}
}
if (pSender == Sender.CLIENT) {
if (receivingTypes.contains(type)) {
receivingTypes.remove(type);
} else {
receivingTypes.add(type);
}
} else {
if (sendingTypes.contains(type)) {
sendingTypes.remove(type);
} else {
sendingTypes.add(type);
}
}
startLogging();
sender.sendMessage(ChatColor.GREEN + "Now logging " + type.getPacketClass().getSimpleName());
return true;
}
sender.sendMessage(ChatColor.RED + "Invalid syntax: /packetlog <protocol> <sender> <packet> [location]");
return true;
} catch (Throwable ex) {
sender.sendMessage(ChatColor.RED + "Failed to parse command: " + ex.toString());
return true;
}
}
private void startLogging() {
manager.removePacketListener(this);
if (sendingTypes.isEmpty() && receivingTypes.isEmpty()) {
return;
}
this.sendingWhitelist = ListeningWhitelist.newBuilder().types(sendingTypes).build();
this.receivingWhitelist = ListeningWhitelist.newBuilder().types(receivingTypes).build();
// Setup the file logger if it hasn't been already
if (location == LogLocation.FILE && fileLogger == null) {
fileLogger = Logger.getLogger("ProtocolLib-FileLogging");
for (Handler handler : fileLogger.getHandlers())
fileLogger.removeHandler(handler);
fileLogger.setUseParentHandlers(false);
try {
File logFile = new File(plugin.getDataFolder(), "log.log");
FileHandler handler = new FileHandler(logFile.getAbsolutePath(), true);
handler.setFormatter(new LogFormatter());
fileLogger.addHandler(handler);
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to obtain log file:", ex);
return;
}
}
manager.addPacketListener(this);
}
@Override
public void onPacketSending(PacketEvent event) {
log(event);
}
@Override
public void onPacketReceiving(PacketEvent event) {
log(event);
}
// Here's where the magic happens
private static String hexDump(byte[] bytes) throws IOException {
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
if (HEX_DUMP == null) {
Class<?> hexDumpClass = MinecraftReflection.getLibraryClass("org.apache.commons.io.HexDump");
HEX_DUMP = Accessors.getMethodAccessor(FuzzyReflection.fromClass(hexDumpClass)
.getMethodByParameters("dump", byte[].class, long.class, OutputStream.class, int.class));
}
HEX_DUMP.invoke(null, bytes, 0, output, 0);
return new String(output.toByteArray(), StandardCharsets.UTF_8);
}
}
private void log(PacketEvent event) {
try {
byte[] bytes = WirePacket.bytesFromPacket(event.getPacket());
String hexDump = hexDump(bytes);
if (location == LogLocation.FILE) {
fileLogger.log(Level.INFO, event.getPacketType() + ":");
fileLogger.log(Level.INFO, hexDump);
fileLogger.log(Level.INFO, "");
} else {
System.out.println(event.getPacketType() + ":");
System.out.println(hexDump);
System.out.println();
}
} catch (Throwable ex) {
plugin.getLogger().log(Level.WARNING, "Failed to log packet " + event.getPacketType() + ":", ex);
plugin.getLogger().log(Level.WARNING, "Clearing packet logger...");
sendingTypes.clear();
receivingTypes.clear();
startLogging();
}
}
@Override
public ListeningWhitelist getSendingWhitelist() {
return sendingWhitelist;
}
@Override
public ListeningWhitelist getReceivingWhitelist() {
return receivingWhitelist;
}
@Override
public Plugin getPlugin() {
return plugin;
}
private enum LogLocation {
CONSOLE, FILE
}
private static class LogFormatter extends Formatter {
private static final SimpleDateFormat DATE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static final String FORMAT = "[{0}] {1}";
@Override
public String format(LogRecord record) {
String string = formatMessage(record);
if (string.isEmpty()) {
return LINE_SEPARATOR;
}
StringBuilder message = new StringBuilder();
message.append(MessageFormat.format(FORMAT, DATE.format(record.getMillis()), string));
message.append(LINE_SEPARATOR);
return message.toString();
}
}
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 Dan Mulloy
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* Logs packets to a given stream
* @author dmulloy2
*/
public class PacketLogging implements CommandExecutor, PacketListener {
public static final String NAME = "packetlog";
private static MethodAccessor HEX_DUMP;
private List<PacketType> sendingTypes = new ArrayList<>();
private List<PacketType> receivingTypes = new ArrayList<>();
private ListeningWhitelist sendingWhitelist;
private ListeningWhitelist receivingWhitelist;
private Logger fileLogger;
private LogLocation location = LogLocation.FILE;
private final ProtocolManager manager;
private final Plugin plugin;
PacketLogging(Plugin plugin, ProtocolManager manager) {
this.plugin = plugin;
this.manager = manager;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
PacketType type = null;
try {
if (args.length > 2) {
Protocol protocol;
try {
protocol = Protocol.valueOf(args[0].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown protocol " + args[0]);
return true;
}
Sender pSender;
try {
pSender = Sender.valueOf(args[1].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown sender: " + args[1]);
return true;
}
try {
try { // Try IDs first
int id = Integer.parseInt(args[2]);
type = PacketType.findCurrent(protocol, pSender, id);
} catch (NumberFormatException ex) { // Check packet names
String name = args[2];
for (PacketType packet : PacketType.values()) {
if (packet.getProtocol() == protocol && packet.getSender() == pSender) {
if (packet.name().equalsIgnoreCase(name)) {
type = packet;
break;
}
for (String className : packet.getClassNames()) {
if (className.equalsIgnoreCase(name)) {
type = packet;
break;
}
}
}
}
}
} catch (IllegalArgumentException ex) { // RIP
type = null;
}
if (type == null) {
sender.sendMessage(ChatColor.RED + "Unknown packet: " + args[2]);
return true;
}
if (args.length > 3) {
if (args[3].equalsIgnoreCase("console")) {
this.location = LogLocation.CONSOLE;
} else {
this.location = LogLocation.FILE;
}
}
if (pSender == Sender.CLIENT) {
if (receivingTypes.contains(type)) {
receivingTypes.remove(type);
} else {
receivingTypes.add(type);
}
} else {
if (sendingTypes.contains(type)) {
sendingTypes.remove(type);
} else {
sendingTypes.add(type);
}
}
startLogging();
sender.sendMessage(ChatColor.GREEN + "Now logging " + type.getPacketClass().getSimpleName());
return true;
}
sender.sendMessage(ChatColor.RED + "Invalid syntax: /packetlog <protocol> <sender> <packet> [location]");
return true;
} catch (Throwable ex) {
sender.sendMessage(ChatColor.RED + "Failed to parse command: " + ex.toString());
return true;
}
}
private void startLogging() {
manager.removePacketListener(this);
if (sendingTypes.isEmpty() && receivingTypes.isEmpty()) {
return;
}
this.sendingWhitelist = ListeningWhitelist.newBuilder().types(sendingTypes).build();
this.receivingWhitelist = ListeningWhitelist.newBuilder().types(receivingTypes).build();
// Setup the file logger if it hasn't been already
if (location == LogLocation.FILE && fileLogger == null) {
fileLogger = Logger.getLogger("ProtocolLib-FileLogging");
for (Handler handler : fileLogger.getHandlers())
fileLogger.removeHandler(handler);
fileLogger.setUseParentHandlers(false);
try {
File logFile = new File(plugin.getDataFolder(), "log.log");
FileHandler handler = new FileHandler(logFile.getAbsolutePath(), true);
handler.setFormatter(new LogFormatter());
fileLogger.addHandler(handler);
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to obtain log file:", ex);
return;
}
}
manager.addPacketListener(this);
}
@Override
public void onPacketSending(PacketEvent event) {
log(event);
}
@Override
public void onPacketReceiving(PacketEvent event) {
log(event);
}
// Here's where the magic happens
private static String hexDump(byte[] bytes) throws IOException {
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
if (HEX_DUMP == null) {
Class<?> hexDumpClass = MinecraftReflection.getLibraryClass("org.apache.commons.io.HexDump");
HEX_DUMP = Accessors.getMethodAccessor(FuzzyReflection.fromClass(hexDumpClass)
.getMethodByParameters("dump", byte[].class, long.class, OutputStream.class, int.class));
}
HEX_DUMP.invoke(null, bytes, 0, output, 0);
return new String(output.toByteArray(), StandardCharsets.UTF_8);
}
}
private void log(PacketEvent event) {
try {
byte[] bytes = WirePacket.bytesFromPacket(event.getPacket());
String hexDump = hexDump(bytes);
if (location == LogLocation.FILE) {
fileLogger.log(Level.INFO, event.getPacketType() + ":");
fileLogger.log(Level.INFO, hexDump);
fileLogger.log(Level.INFO, "");
} else {
System.out.println(event.getPacketType() + ":");
System.out.println(hexDump);
System.out.println();
}
} catch (Throwable ex) {
plugin.getLogger().log(Level.WARNING, "Failed to log packet " + event.getPacketType() + ":", ex);
plugin.getLogger().log(Level.WARNING, "Clearing packet logger...");
sendingTypes.clear();
receivingTypes.clear();
startLogging();
}
}
@Override
public ListeningWhitelist getSendingWhitelist() {
return sendingWhitelist;
}
@Override
public ListeningWhitelist getReceivingWhitelist() {
return receivingWhitelist;
}
@Override
public Plugin getPlugin() {
return plugin;
}
private enum LogLocation {
CONSOLE, FILE
}
private static class LogFormatter extends Formatter {
private static final SimpleDateFormat DATE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static final String FORMAT = "[{0}] {1}";
@Override
public String format(LogRecord record) {
String string = formatMessage(record);
if (string.isEmpty()) {
return LINE_SEPARATOR;
}
StringBuilder message = new StringBuilder();
message.append(MessageFormat.format(FORMAT, DATE.format(record.getMillis()), string));
message.append(LINE_SEPARATOR);
return message.toString();
}
}
}
@@ -114,102 +114,101 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x09, "BlockChange", "SPacketBlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0A, "Boss", "SPacketUpdateBossInfo");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0B, "ServerDifficulty", "SPacketServerDifficulty");
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x0C, "ChatPreview");
public static final PacketType CLEAR_TITLES = new PacketType(PROTOCOL, SENDER, 0x0D, "ClearTitles");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0E, "TabComplete", "SPacketTabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x0F, "Commands");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x10, "CloseWindow", "SPacketCloseWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x11, "WindowItems", "SPacketWindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x12, "WindowData", "SPacketWindowProperty");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x13, "SetSlot", "SPacketSetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x14, "SetCooldown", "SPacketCooldown");
public static final PacketType CUSTOM_CHAT_COMPLETIONS = new PacketType(PROTOCOL, SENDER, 0x15, "CustomChatCompletions");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x16, "CustomPayload", "SPacketCustomPayload");
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x17, "CustomSoundEffect", "SPacketCustomSound");
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x18, "DeleteChat");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x19, "KickDisconnect", "SPacketDisconnect");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1A, "EntityStatus", "SPacketEntityStatus");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1B, "Explosion", "SPacketExplosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1C, "UnloadChunk", "SPacketUnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1D, "GameStateChange", "SPacketChangeGameState");
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x1E, "OpenWindowHorse");
public static final PacketType INITIALIZE_BORDER = new PacketType(PROTOCOL, SENDER, 0x1F, "InitializeBorder");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x20, "KeepAlive", "SPacketKeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x21, "MapChunk", "SPacketChunkData", "LevelChunkWithLight");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x22, "WorldEvent", "SPacketEffect");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x23, "WorldParticles", "SPacketParticles");
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x24, "LightUpdate");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x25, "Login", "SPacketJoinGame");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x26, "Map", "SPacketMaps");
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x27, "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x28, "Entity$PacketPlayOutRelEntityMove", "Entity$RelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x29, "Entity$PacketPlayOutRelEntityMoveLook", "Entity$RelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x2A, "Entity$PacketPlayOutEntityLook", "Entity$EntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2B, "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x2C, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x2D, "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2E, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x2F, "Ping");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x30, "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x31, "Abilities", "SPacketPlayerAbilities");
public static final PacketType PLAYER_CHAT_HEADER = new PacketType(PROTOCOL, SENDER, 0x32, "PlayerChatHeader");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x33, "PlayerChat", "Chat", "SPacketChat");
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x34, "PlayerCombatEnd");
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x35, "PlayerCombatEnter");
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x36, "PlayerCombatKill");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x37, "PlayerInfo", "SPacketPlayerListItem");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x38, "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x39, "Position");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x3A, "Recipes", "SPacketRecipeBook");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x3B, "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x3C, "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x3D, "ResourcePackSend", "SPacketResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x3E, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x3F, "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x40, "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x41, "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x42, "ServerData");
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x43, "SetActionBarText");
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x44, "SetBorderCenter");
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x45, "SetBorderLerpSize");
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x46, "SetBorderSize");
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x47, "SetBorderWarningDelay");
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x48, "SetBorderWarningDistance");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x49, "Camera", "SPacketCamera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x4A, "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x4B, "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x4C, "ViewDistance");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x4D, "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SET_DISPLAY_CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x4E, "SetDisplayChatPreview");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x4F, "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x50, "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x51, "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x52, "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x53, "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x54, "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x55, "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x56, "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x57, "Mount", "SPacketSetPassengers");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x58, "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x59, "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x5A, "SetSimulationDistance");
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5B, "SetSubtitleText");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x5C, "UpdateTime", "SPacketTimeUpdate");
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5D, "SetTitleText");
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x5E, "SetTitlesAnimation");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x5F, "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x60, "NamedSoundEffect");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x61, "StopSound");
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x62, "SystemChat");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x63, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x64, "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x65, "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x66, "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x67, "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x68, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x69, "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x6A, "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x6B, "Tags");
public static final PacketType CLEAR_TITLES = new PacketType(PROTOCOL, SENDER, 0x0C, "ClearTitles");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0D, "TabComplete", "SPacketTabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x0E, "Commands");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0F, "CloseWindow", "SPacketCloseWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x10, "WindowItems", "SPacketWindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x11, "WindowData", "SPacketWindowProperty");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x12, "SetSlot", "SPacketSetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x13, "SetCooldown", "SPacketCooldown");
public static final PacketType CUSTOM_CHAT_COMPLETIONS = new PacketType(PROTOCOL, SENDER, 0x14, "CustomChatCompletions");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x15, "CustomPayload", "SPacketCustomPayload");
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x16, "DeleteChat");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x17, "KickDisconnect", "SPacketDisconnect");
public static final PacketType DISGUISED_CHAT = new PacketType(PROTOCOL, SENDER, 0x18, "DisguisedChat");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x19, "EntityStatus", "SPacketEntityStatus");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1A, "Explosion", "SPacketExplosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1B, "UnloadChunk", "SPacketUnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1C, "GameStateChange", "SPacketChangeGameState");
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x1D, "OpenWindowHorse");
public static final PacketType INITIALIZE_BORDER = new PacketType(PROTOCOL, SENDER, 0x1E, "InitializeBorder");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x1F, "KeepAlive", "SPacketKeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x20, "LevelChunkWithLight", "MapChunk", "SPacketChunkData");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x21, "WorldEvent", "SPacketEffect");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x22, "WorldParticles", "SPacketParticles");
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x23, "LightUpdate");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x24, "Login", "SPacketJoinGame");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x25, "Map", "SPacketMaps");
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x26, "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x27, "Entity$RelEntityMove", "Entity$PacketPlayOutRelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x28, "Entity$RelEntityMoveLook", "Entity$PacketPlayOutRelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x29, "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2A, "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x2B, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x2C, "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2D, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x2E, "Ping");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x2F, "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x30, "Abilities", "SPacketPlayerAbilities");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x31, "PlayerChat", "Chat", "SPacketChat");
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x32, "PlayerCombatEnd");
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x33, "PlayerCombatEnter");
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x34, "PlayerCombatKill");
public static final PacketType PLAYER_INFO_REMOVE = new PacketType(PROTOCOL, SENDER, 0x35, "PlayerInfoRemove");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x36, "PlayerInfoUpdate", "PlayerInfo");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x37, "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x38, "Position");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x39, "Recipes", "SPacketRecipeBook");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x3A, "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x3B, "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x3C, "ResourcePackSend", "SPacketResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x3D, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x3E, "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x3F, "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x40, "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x41, "ServerData");
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x42, "SetActionBarText");
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x43, "SetBorderCenter");
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x44, "SetBorderLerpSize");
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x45, "SetBorderSize");
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x46, "SetBorderWarningDelay");
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x47, "SetBorderWarningDistance");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x48, "Camera", "SPacketCamera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x49, "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x4A, "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x4B, "ViewDistance");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x4C, "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x4D, "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x4E, "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x4F, "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x50, "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x51, "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x52, "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x53, "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x54, "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x55, "Mount", "SPacketSetPassengers");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x56, "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x57, "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x58, "SetSimulationDistance");
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x59, "SetSubtitleText");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x5A, "UpdateTime", "SPacketTimeUpdate");
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5B, "SetTitleText");
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x5C, "SetTitlesAnimation");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x5D, "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x5E, "NamedSoundEffect");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x5F, "StopSound");
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x60, "SystemChat");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x61, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x62, "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x63, "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x64, "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x65, "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x66, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType UPDATE_ENABLED_FEATURES = new PacketType(PROTOCOL, SENDER, 0x67, "UpdateEnabledFeatures");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x68, "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x69, "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x6A, "Tags");
// ---- Removed in 1.9
@@ -340,6 +339,30 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@Deprecated
public static final PacketType BLOCK_BREAK = new PacketType(PROTOCOL, SENDER, 241, "BlockBreak");
/**
* @deprecated Removed in 1.19.3
*/
@Deprecated
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x0C, "ChatPreview");
/**
* @deprecated Removed in 1.19.3
*/
@Deprecated
public static final PacketType PLAYER_CHAT_HEADER = new PacketType(PROTOCOL, SENDER, 0x32, "PlayerChatHeader");
/**
* @deprecated Removed in 1.19.3
*/
@Deprecated
public static final PacketType SET_DISPLAY_CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x4E, "SetDisplayChatPreview");
/**
* @deprecated Removed in 1.19.3
*/
@Deprecated
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x16, "CustomSoundEffect", "SPacketCustomSound");
private final static Server INSTANCE = new Server();
// Prevent accidental construction
@@ -366,33 +389,33 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType CHAT_ACK = new PacketType(PROTOCOL, SENDER, 0x03, "ChatAck");
public static final PacketType CHAT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x04, "ChatCommand");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x05, "Chat", "CPacketChatMessage");
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x06, "ChatPreview");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x07, "ClientCommand", "CPacketClientStatus");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x08, "Settings", "CPacketClientSettings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x09, "TabComplete", "CPacketTabComplete");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x0A, "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x0B, "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0C, "CloseWindow", "CPacketCloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0D, "CustomPayload", "CPacketCustomPayload");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x0E, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0F, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x10, "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x11, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x12, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x13, "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x14, "Flying$PacketPlayInPosition", "Flying$Position", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x15, "Flying$PacketPlayInPositionLook", "Flying$PositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x16, "Flying$PacketPlayInLook", "Flying$Look", "CPacketPlayer$Rotation");
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x17, "Flying$d");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x18, "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x19, "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x1A, "PickItem");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x1B, "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x1C, "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x1D, "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x1E, "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1F, "SteerVehicle", "CPacketInput");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x20, "Pong", "ServerboundPongPacket");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x06, "ClientCommand", "CPacketClientStatus");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x07, "Settings", "CPacketClientSettings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x08, "TabComplete", "CPacketTabComplete");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x09, "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x0A, "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0B, "CloseWindow", "CPacketCloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0C, "CustomPayload", "CPacketCustomPayload");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x0D, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0E, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0F, "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x10, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x11, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x12, "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x13, "Flying$PacketPlayInPosition", "Flying$Position", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x14, "Flying$PacketPlayInPositionLook", "Flying$PositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x15, "Flying$PacketPlayInLook", "Flying$Look", "CPacketPlayer$Rotation");
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x16, "Flying$d");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x17, "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x18, "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x19, "PickItem");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x1A, "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x1B, "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x1C, "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x1D, "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1E, "SteerVehicle", "CPacketInput");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x1F, "Pong");
public static final PacketType CHAT_SESSION_UPDATE = new PacketType(PROTOCOL, SENDER, 0x20, "ChatSessionUpdate");
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x21, "RecipeSettings");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x22, "RecipeDisplayed", "CPacketRecipeInfo");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x23, "ItemName");
@@ -424,6 +447,12 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@Deprecated
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 254, "Flying", "CPacketPlayer");
/**
* @deprecated Removed in 1.19.3
*/
@Deprecated
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x06, "ChatPreview");
private final static Client INSTANCE = new Client();
// Prevent accidental construction
@@ -397,6 +397,17 @@ public abstract class AbstractStructure {
BukkitConverters.getListConverter(BukkitConverters.getWatchableObjectConverter()));
}
/**
* Retrieves a read/write structure for collections of data values.
* @return A modifier for data values.
*/
public StructureModifier<List<WrappedDataValue>> getDataValueCollectionModifier() {
// Convert to and from the ProtocolLib wrapper
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(BukkitConverters.getDataValueConverter()));
}
/**
* Retrieves a read/write structure for block fields.
* <p>
@@ -625,6 +636,20 @@ public abstract class AbstractStructure {
EnumWrappers.getPlayerInfoActionConverter());
}
/**
* Retrieve a read/write structure for an EnumSet of PlayerInfos.
* @return A modifier for an EnumSet of PlayerInfo fields.
*/
public StructureModifier<Set<EnumWrappers.PlayerInfoAction>> getPlayerInfoActions() {
// Convert to and from the wrapper
return structureModifier.withType(
EnumSet.class,
Converters.collection(
EnumWrappers.getPlayerInfoActionConverter(),
generic -> EnumSet.noneOf(EnumWrappers.PlayerInfoAction.class),
specific -> EnumWrappers.createEmptyEnumSet(EnumWrappers.getPlayerInfoActionClass())));
}
/**
* Retrieve a read/write structure for the TitleAction enum in 1.8.
* @return A modifier for TitleAction enum fields.
@@ -1,143 +1,143 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.events;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.commons.lang.Validate;
/**
* Stores and retrieves metadata for applicable packet objects.
* @author dmulloy2
*/
class PacketMetadata {
private static class MetaObject<T> {
private final String key;
private final T value;
private MetaObject(String key, T value) {
this.key = key;
this.value = value;
}
@Override
public int hashCode() {
return Objects.hash(key, value);
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o instanceof MetaObject) {
MetaObject that = (MetaObject) o;
return that.key.equals(this.key) &&
that.value.equals(this.value);
}
return false;
}
@Override
public String toString() {
return "MetaObject[" + key + "=" + value + "]";
}
}
// Packet meta cache
private static Cache<Object, List<MetaObject>> META_CACHE;
public static <T> Optional<T> get(Object packet, String key) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
return Optional.empty();
}
List<MetaObject> meta = META_CACHE.getIfPresent(packet);
if (meta == null) {
return Optional.empty();
}
for (MetaObject object : meta) {
if (object.key.equals(key)) {
return Optional.of((T) object.value);
}
}
return Optional.empty();
}
private static void createCache() {
META_CACHE = CacheBuilder
.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();
}
public static <T> void set(Object packet, String key, T value) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
createCache();
}
List<MetaObject> packetMeta;
try {
packetMeta = META_CACHE.get(packet, ArrayList::new);
} catch (ExecutionException ex) {
// Not possible, but let's humor the array list constructor having an issue
packetMeta = new ArrayList<>();
}
packetMeta.removeIf(meta -> meta.key.equals(key));
packetMeta.add(new MetaObject<>(key, value));
META_CACHE.put(packet, packetMeta);
}
public static <T> Optional<T> remove(Object packet, String key) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
return Optional.empty();
}
List<MetaObject> packetMeta = META_CACHE.getIfPresent(packet);
if (packetMeta == null) {
return Optional.empty();
}
Optional<T> value = Optional.empty();
Iterator<MetaObject> iter = packetMeta.iterator();
while (iter.hasNext()) {
MetaObject meta = iter.next();
if (meta.key.equals(key)) {
value = Optional.of((T) meta.value);
iter.remove();
}
}
return value;
}
}
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.events;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.commons.lang.Validate;
/**
* Stores and retrieves metadata for applicable packet objects.
* @author dmulloy2
*/
class PacketMetadata {
private static class MetaObject<T> {
private final String key;
private final T value;
private MetaObject(String key, T value) {
this.key = key;
this.value = value;
}
@Override
public int hashCode() {
return Objects.hash(key, value);
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o instanceof MetaObject) {
MetaObject that = (MetaObject) o;
return that.key.equals(this.key) &&
that.value.equals(this.value);
}
return false;
}
@Override
public String toString() {
return "MetaObject[" + key + "=" + value + "]";
}
}
// Packet meta cache
private static Cache<Object, List<MetaObject>> META_CACHE;
public static <T> Optional<T> get(Object packet, String key) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
return Optional.empty();
}
List<MetaObject> meta = META_CACHE.getIfPresent(packet);
if (meta == null) {
return Optional.empty();
}
for (MetaObject object : meta) {
if (object.key.equals(key)) {
return Optional.of((T) object.value);
}
}
return Optional.empty();
}
private static void createCache() {
META_CACHE = CacheBuilder
.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();
}
public static <T> void set(Object packet, String key, T value) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
createCache();
}
List<MetaObject> packetMeta;
try {
packetMeta = META_CACHE.get(packet, ArrayList::new);
} catch (ExecutionException ex) {
// Not possible, but let's humor the array list constructor having an issue
packetMeta = new ArrayList<>();
}
packetMeta.removeIf(meta -> meta.key.equals(key));
packetMeta.add(new MetaObject<>(key, value));
META_CACHE.put(packet, packetMeta);
}
public static <T> Optional<T> remove(Object packet, String key) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
return Optional.empty();
}
List<MetaObject> packetMeta = META_CACHE.getIfPresent(packet);
if (packetMeta == null) {
return Optional.empty();
}
Optional<T> value = Optional.empty();
Iterator<MetaObject> iter = packetMeta.iterator();
while (iter.hasNext()) {
MetaObject meta = iter.next();
if (meta.key.equals(key)) {
value = Optional.of((T) meta.value);
iter.remove();
}
}
return value;
}
}
@@ -17,6 +17,7 @@
package com.comphenix.protocol.injector;
import java.security.PublicKey;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
@@ -36,7 +37,8 @@ import com.comphenix.protocol.utility.ZeroBuffer;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy.Default;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;
@@ -53,7 +55,8 @@ public class StructureCache {
// packet data serializer which always returns an empty nbt tag compound
private static boolean TRICK_TRIED = false;
private static ConstructorAccessor TRICKED_DATA_SERIALIZER;
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_BASE;
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_JSON;
public static Object newPacket(Class<?> packetClass) {
Supplier<Object> packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, clazz -> {
@@ -63,15 +66,28 @@ public class StructureCache {
clazz,
MinecraftReflection.getPacketDataSerializerClass());
if (serializerAccessor != null) {
return () -> {
// the tricked serializer should always be present... If not try using a normal buffer
Object dataSerializer = getTrickDataSerializerOrNull();
if (dataSerializer == null) {
dataSerializer = MinecraftReflection.getPacketDataSerializer(new ZeroBuffer());
}
// check if the method is possible
if (tryInitTrickDataSerializer()) {
try {
// first try with the base accessor
Object serializer = TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer());
serializerAccessor.invoke(serializer); // throwaway instance, for testing
return serializerAccessor.invoke(dataSerializer);
};
// method is working
return () -> serializerAccessor.invoke(serializer);
} catch (Exception exception) {
try {
// try with the json accessor
Object serializer = TRICKED_DATA_SERIALIZER_JSON.invoke(new ZeroBuffer());
serializerAccessor.invoke(serializer); // throwaway instance, for testing
// method is working
return () -> serializerAccessor.invoke(serializer);
} catch (Exception ignored) {
// shrug, fall back to default behaviour
}
}
}
}
}
@@ -123,38 +139,64 @@ public class StructureCache {
});
}
/**
* Returns a new mocked null data serializer instance, if possible.
*
* @return a new null data serializer instance.
*/
public static Object newNullDataSerializer() {
tryInitTrickDataSerializer();
return TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer());
}
/**
* Creates a packet data serializer sub-class if needed to allow the fixed read of a NbtTagCompound because of a
* null check in the MapChunk packet constructor.
*
* @return an accessor to a constructor which creates a data serializer.
*/
public static Object getTrickDataSerializerOrNull() {
if (TRICKED_DATA_SERIALIZER == null && !TRICK_TRIED) {
// ensure that we only try once to create the class
TRICK_TRIED = true;
try {
// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
Object textCompound = WrappedChatComponent.fromText("").getHandle();
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
// create the method in the class to read an empty nbt tag compound (currently used for MAP_CHUNK because of null check)
Class<?> generatedClass = ByteBuddyFactory.getInstance()
.createSubclass(MinecraftReflection.getPacketDataSerializerClass())
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializer")
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
.intercept(FixedValue.value(compound))
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
.intercept(FixedValue.value(textCompound))
.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER = Accessors.getConstructorAccessor(generatedClass, ByteBuf.class);
} catch (Exception ignored) {
// can happen if unsupported
}
public static boolean tryInitTrickDataSerializer() {
if (TRICK_TRIED) {
return TRICKED_DATA_SERIALIZER_BASE != null;
}
return TRICKED_DATA_SERIALIZER == null ? null : TRICKED_DATA_SERIALIZER.invoke(new ZeroBuffer());
// prevent double init
TRICK_TRIED = true;
try {
// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
Object textCompound = WrappedChatComponent.fromText("").getHandle();
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
// base builder which intercepts a few methods
DynamicType.Builder<?> baseBuilder = ByteBuddyFactory.getInstance()
.createSubclass(MinecraftReflection.getPacketDataSerializerClass())
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
.intercept(FixedValue.value(compound))
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
.intercept(FixedValue.value(textCompound))
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
.intercept(FixedValue.nullValue());
Class<?> serializerBase = baseBuilder.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER_BASE = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
// extended builder which intercepts the read string method as well
Class<?> withStringIntercept = baseBuilder
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson")
.method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class)))
.intercept(FixedValue.value("{}"))
.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER_JSON = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);
// worked
return true;
} catch (Exception ignored) {
}
// didn't work
return false;
}
}
@@ -116,7 +116,9 @@ public class FuzzyReflection {
public static <T> Set<T> combineArrays(T[]... arrays) {
Set<T> result = new LinkedHashSet<>();
for (T[] elements : arrays) {
Collections.addAll(result, elements);
if (elements != null) {
Collections.addAll(result, elements);
}
}
return result;
@@ -1,43 +1,43 @@
/**
* (c) 2018 dmulloy2
*/
package com.comphenix.protocol.reflect.cloning;
import java.util.Optional;
import java.util.OptionalInt;
/**
* A cloner that can clone Java Optional objects
* @author dmulloy2
*/
public class JavaOptionalCloner implements Cloner {
protected Cloner wrapped;
public JavaOptionalCloner(Cloner wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean canClone(Object source) {
return source instanceof Optional || source instanceof OptionalInt;
}
@Override
public Object clone(Object source) {
if (source instanceof Optional) {
Optional<?> optional = (Optional<?>) source;
return optional.map(o -> wrapped.clone(o));
} else if (source instanceof OptionalInt) {
// why Java felt the need to make each optional class distinct is beyond me
// like why couldn't they have given us at least a common interface or something
OptionalInt optional = (OptionalInt) source;
return optional.isPresent() ? OptionalInt.of(optional.getAsInt()) : OptionalInt.empty();
}
return null;
}
public Cloner getWrapped() {
return wrapped;
}
}
/**
* (c) 2018 dmulloy2
*/
package com.comphenix.protocol.reflect.cloning;
import java.util.Optional;
import java.util.OptionalInt;
/**
* A cloner that can clone Java Optional objects
* @author dmulloy2
*/
public class JavaOptionalCloner implements Cloner {
protected Cloner wrapped;
public JavaOptionalCloner(Cloner wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean canClone(Object source) {
return source instanceof Optional || source instanceof OptionalInt;
}
@Override
public Object clone(Object source) {
if (source instanceof Optional) {
Optional<?> optional = (Optional<?>) source;
return optional.map(o -> wrapped.clone(o));
} else if (source instanceof OptionalInt) {
// why Java felt the need to make each optional class distinct is beyond me
// like why couldn't they have given us at least a common interface or something
OptionalInt optional = (OptionalInt) source;
return optional.isPresent() ? OptionalInt.of(optional.getAsInt()) : OptionalInt.empty();
}
return null;
}
public Cloner getWrapped() {
return wrapped;
}
}
@@ -84,6 +84,7 @@ public final class MinecraftProtocolVersion {
map.put(new MinecraftVersion(1, 19, 0), 759);
map.put(new MinecraftVersion(1, 19, 1), 760);
map.put(new MinecraftVersion(1, 19, 2), 760);
map.put(new MinecraftVersion(1, 19, 3), 761);
return map;
}
File diff suppressed because it is too large. Load diff
@@ -1,421 +1,426 @@
/*
* 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.utility;
import com.comphenix.protocol.ProtocolLibrary;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Ordering;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.Bukkit;
import org.bukkit.Server;
/**
* Determine the current Minecraft version.
*
* @author Kristian
*/
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
/**
* Version 1.19 - the wild update
*/
public static final MinecraftVersion WILD_UPDATE = new MinecraftVersion("1.19");
/**
* Version 1.18 - caves and cliffs part 2
*/
public static final MinecraftVersion CAVES_CLIFFS_2 = new MinecraftVersion("1.18");
/**
* Version 1.17 - caves and cliffs part 1
*/
public static final MinecraftVersion CAVES_CLIFFS_1 = new MinecraftVersion("1.17");
/**
* Version 1.16.2 - breaking change to the nether update
*/
public static final MinecraftVersion NETHER_UPDATE_2 = new MinecraftVersion("1.16.2");
/**
* Version 1.16.0 - the nether update
*/
public static final MinecraftVersion NETHER_UPDATE = new MinecraftVersion("1.16");
/**
* Version 1.15 - the bee update
*/
public static final MinecraftVersion BEE_UPDATE = new MinecraftVersion("1.15");
/**
* Version 1.14 - village and pillage update.
*/
public static final MinecraftVersion VILLAGE_UPDATE = new MinecraftVersion("1.14");
/**
* Version 1.13 - update aquatic.
*/
public static final MinecraftVersion AQUATIC_UPDATE = new MinecraftVersion("1.13");
/**
* Version 1.12 - the world of color update.
*/
public static final MinecraftVersion COLOR_UPDATE = new MinecraftVersion("1.12");
/**
* Version 1.11 - the exploration update.
*/
public static final MinecraftVersion EXPLORATION_UPDATE = new MinecraftVersion("1.11");
/**
* Version 1.10 - the frostburn update.
*/
public static final MinecraftVersion FROSTBURN_UPDATE = new MinecraftVersion("1.10");
/**
* Version 1.9 - the combat update.
*/
public static final MinecraftVersion COMBAT_UPDATE = new MinecraftVersion("1.9");
/**
* Version 1.8 - the "bountiful" update.
*/
public static final MinecraftVersion BOUNTIFUL_UPDATE = new MinecraftVersion("1.8");
/**
* Version 1.7.8 - the update that changed the skin format (and distribution - R.I.P. player disguise)
*/
public static final MinecraftVersion SKIN_UPDATE = new MinecraftVersion("1.7.8");
/**
* Version 1.7.2 - the update that changed the world.
*/
public static final MinecraftVersion WORLD_UPDATE = new MinecraftVersion("1.7.2");
/**
* Version 1.6.1 - the horse update.
*/
public static final MinecraftVersion HORSE_UPDATE = new MinecraftVersion("1.6.1");
/**
* Version 1.5.0 - the redstone update.
*/
public static final MinecraftVersion REDSTONE_UPDATE = new MinecraftVersion("1.5.0");
/**
* Version 1.4.2 - the scary update (Wither Boss).
*/
public static final MinecraftVersion SCARY_UPDATE = new MinecraftVersion("1.4.2");
/**
* The latest release version of minecraft.
*/
public static final MinecraftVersion LATEST = WILD_UPDATE;
// used when serializing
private static final long serialVersionUID = -8695133558996459770L;
/**
* Regular expression used to parse version strings.
*/
private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-.]+).*");
/**
* The current version of minecraft, lazy initialized by MinecraftVersion.currentVersion()
*/
private static MinecraftVersion currentVersion;
private final int major;
private final int minor;
private final int build;
// The development stage
private final String development;
// Snapshot?
private final SnapshotVersion snapshot;
private volatile Boolean atCurrentOrAbove;
/**
* Determine the current Minecraft version.
*
* @param server - the Bukkit server that will be used to examine the MC version.
*/
public MinecraftVersion(Server server) {
this(extractVersion(server.getVersion()));
}
/**
* Construct a version object from the format major.minor.build, or the snapshot format.
*
* @param versionOnly - the version in text form.
*/
public MinecraftVersion(String versionOnly) {
this(versionOnly, true);
}
/**
* Construct a version format from the standard release version or the snapshot verison.
*
* @param versionOnly - the version.
* @param parseSnapshot - TRUE to parse the snapshot, FALSE otherwise.
*/
private MinecraftVersion(String versionOnly, boolean parseSnapshot) {
String[] section = versionOnly.split("-");
SnapshotVersion snapshot = null;
int[] numbers = new int[3];
try {
numbers = this.parseVersion(section[0]);
} catch (NumberFormatException cause) {
// Skip snapshot parsing
if (!parseSnapshot) {
throw cause;
}
try {
// Determine if the snapshot is newer than the current release version
snapshot = new SnapshotVersion(section[0]);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
MinecraftVersion latest = new MinecraftVersion(ProtocolLibrary.MAXIMUM_MINECRAFT_VERSION, false);
boolean newer = snapshot.getSnapshotDate().compareTo(
format.parse(ProtocolLibrary.MINECRAFT_LAST_RELEASE_DATE)) > 0;
numbers[0] = latest.getMajor();
numbers[1] = latest.getMinor() + (newer ? 1 : -1);
} catch (Exception e) {
throw new IllegalStateException("Cannot parse " + section[0], e);
}
}
this.major = numbers[0];
this.minor = numbers[1];
this.build = numbers[2];
this.development = section.length > 1 ? section[1] : (snapshot != null ? "snapshot" : null);
this.snapshot = snapshot;
}
/**
* Construct a version object directly.
*
* @param major - major version number.
* @param minor - minor version number.
* @param build - build version number.
*/
public MinecraftVersion(int major, int minor, int build) {
this(major, minor, build, null);
}
/**
* Construct a version object directly.
*
* @param major - major version number.
* @param minor - minor version number.
* @param build - build version number.
* @param development - development stage.
*/
public MinecraftVersion(int major, int minor, int build, String development) {
this.major = major;
this.minor = minor;
this.build = build;
this.development = development;
this.snapshot = null;
}
/**
* Extract the Minecraft version from CraftBukkit itself.
*
* @param text - the server version in text form.
* @return The underlying MC version.
* @throws IllegalStateException If we could not parse the version string.
*/
public static String extractVersion(String text) {
Matcher version = VERSION_PATTERN.matcher(text);
if (version.matches() && version.group(1) != null) {
return version.group(1);
} else {
throw new IllegalStateException("Cannot parse version String '" + text + "'");
}
}
/**
* Parse the given server version into a Minecraft version.
*
* @param serverVersion - the server version.
* @return The resulting Minecraft version.
*/
public static MinecraftVersion fromServerVersion(String serverVersion) {
return new MinecraftVersion(extractVersion(serverVersion));
}
public static MinecraftVersion getCurrentVersion() {
if (currentVersion == null) {
currentVersion = fromServerVersion(Bukkit.getVersion());
}
return currentVersion;
}
public static void setCurrentVersion(MinecraftVersion version) {
currentVersion = version;
}
public static boolean atOrAbove(MinecraftVersion version) {
return getCurrentVersion().isAtLeast(version);
}
private int[] parseVersion(String version) {
String[] elements = version.split("\\.");
int[] numbers = new int[3];
// Make sure it's even a valid version
if (elements.length < 1) {
throw new IllegalStateException("Corrupt MC version: " + version);
}
// The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively.
for (int i = 0; i < Math.min(numbers.length, elements.length); i++) {
numbers[i] = Integer.parseInt(elements[i].trim());
}
return numbers;
}
/**
* Major version number
*
* @return Current major version number.
*/
public int getMajor() {
return this.major;
}
/**
* Minor version number
*
* @return Current minor version number.
*/
public int getMinor() {
return this.minor;
}
/**
* Build version number
*
* @return Current build version number.
*/
public int getBuild() {
return this.build;
}
/**
* Retrieve the development stage.
*
* @return Development stage, or NULL if this is a release.
*/
public String getDevelopmentStage() {
return this.development;
}
/**
* Retrieve the snapshot version, or NULL if this is a release.
*
* @return The snapshot version.
*/
public SnapshotVersion getSnapshot() {
return this.snapshot;
}
/**
* Determine if this version is a snapshot.
*
* @return The snapshot version.
*/
public boolean isSnapshot() {
return this.snapshot != null;
}
/**
* Checks if this version is at or above the current version the server is running.
*
* @return true if this version is equal or newer than the server version, false otherwise.
*/
public boolean atOrAbove() {
if (this.atCurrentOrAbove == null) {
this.atCurrentOrAbove = MinecraftVersion.atOrAbove(this);
}
return this.atCurrentOrAbove;
}
/**
* Retrieve the version String (major.minor.build) only.
*
* @return A normal version string.
*/
public String getVersion() {
if (this.getDevelopmentStage() == null) {
return String.format("%s.%s.%s", this.getMajor(), this.getMinor(), this.getBuild());
} else {
return String.format("%s.%s.%s-%s%s", this.getMajor(), this.getMinor(), this.getBuild(),
this.getDevelopmentStage(), this.isSnapshot() ? this.snapshot : "");
}
}
@Override
public int compareTo(MinecraftVersion o) {
if (o == null) {
return 1;
}
return ComparisonChain.start()
.compare(this.getMajor(), o.getMajor())
.compare(this.getMinor(), o.getMinor())
.compare(this.getBuild(), o.getBuild())
.compare(this.getDevelopmentStage(), o.getDevelopmentStage(), Ordering.natural().nullsLast())
.compare(this.getSnapshot(), o.getSnapshot(), Ordering.natural().nullsFirst())
.result();
}
public boolean isAtLeast(MinecraftVersion other) {
if (other == null) {
return false;
}
return this.compareTo(other) >= 0;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj instanceof MinecraftVersion) {
MinecraftVersion other = (MinecraftVersion) obj;
return this.getMajor() == other.getMajor() &&
this.getMinor() == other.getMinor() &&
this.getBuild() == other.getBuild() &&
Objects.equals(this.getDevelopmentStage(), other.getDevelopmentStage());
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(this.getMajor(), this.getMinor(), this.getBuild());
}
@Override
public String toString() {
// Convert to a String that we can parse back again
return String.format("(MC: %s)", this.getVersion());
}
}
/*
* 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.utility;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.comphenix.protocol.ProtocolLibrary;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Ordering;
import org.bukkit.Bukkit;
import org.bukkit.Server;
/**
* Determine the current Minecraft version.
*
* @author Kristian
*/
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
/**
* Version 1.19.3 - introducing feature preview
*/
public static final MinecraftVersion FEATURE_PREVIEW_UPDATE = new MinecraftVersion("1.19.3");
/**
* Version 1.19 - the wild update
*/
public static final MinecraftVersion WILD_UPDATE = new MinecraftVersion("1.19");
/**
* Version 1.18 - caves and cliffs part 2
*/
public static final MinecraftVersion CAVES_CLIFFS_2 = new MinecraftVersion("1.18");
/**
* Version 1.17 - caves and cliffs part 1
*/
public static final MinecraftVersion CAVES_CLIFFS_1 = new MinecraftVersion("1.17");
/**
* Version 1.16.2 - breaking change to the nether update
*/
public static final MinecraftVersion NETHER_UPDATE_2 = new MinecraftVersion("1.16.2");
/**
* Version 1.16.0 - the nether update
*/
public static final MinecraftVersion NETHER_UPDATE = new MinecraftVersion("1.16");
/**
* Version 1.15 - the bee update
*/
public static final MinecraftVersion BEE_UPDATE = new MinecraftVersion("1.15");
/**
* Version 1.14 - village and pillage update.
*/
public static final MinecraftVersion VILLAGE_UPDATE = new MinecraftVersion("1.14");
/**
* Version 1.13 - update aquatic.
*/
public static final MinecraftVersion AQUATIC_UPDATE = new MinecraftVersion("1.13");
/**
* Version 1.12 - the world of color update.
*/
public static final MinecraftVersion COLOR_UPDATE = new MinecraftVersion("1.12");
/**
* Version 1.11 - the exploration update.
*/
public static final MinecraftVersion EXPLORATION_UPDATE = new MinecraftVersion("1.11");
/**
* Version 1.10 - the frostburn update.
*/
public static final MinecraftVersion FROSTBURN_UPDATE = new MinecraftVersion("1.10");
/**
* Version 1.9 - the combat update.
*/
public static final MinecraftVersion COMBAT_UPDATE = new MinecraftVersion("1.9");
/**
* Version 1.8 - the "bountiful" update.
*/
public static final MinecraftVersion BOUNTIFUL_UPDATE = new MinecraftVersion("1.8");
/**
* Version 1.7.8 - the update that changed the skin format (and distribution - R.I.P. player disguise)
*/
public static final MinecraftVersion SKIN_UPDATE = new MinecraftVersion("1.7.8");
/**
* Version 1.7.2 - the update that changed the world.
*/
public static final MinecraftVersion WORLD_UPDATE = new MinecraftVersion("1.7.2");
/**
* Version 1.6.1 - the horse update.
*/
public static final MinecraftVersion HORSE_UPDATE = new MinecraftVersion("1.6.1");
/**
* Version 1.5.0 - the redstone update.
*/
public static final MinecraftVersion REDSTONE_UPDATE = new MinecraftVersion("1.5.0");
/**
* Version 1.4.2 - the scary update (Wither Boss).
*/
public static final MinecraftVersion SCARY_UPDATE = new MinecraftVersion("1.4.2");
/**
* The latest release version of minecraft.
*/
public static final MinecraftVersion LATEST = FEATURE_PREVIEW_UPDATE;
// used when serializing
private static final long serialVersionUID = -8695133558996459770L;
/**
* Regular expression used to parse version strings.
*/
private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-.]+).*");
/**
* The current version of minecraft, lazy initialized by MinecraftVersion.currentVersion()
*/
private static MinecraftVersion currentVersion;
private final int major;
private final int minor;
private final int build;
// The development stage
private final String development;
// Snapshot?
private final SnapshotVersion snapshot;
private volatile Boolean atCurrentOrAbove;
/**
* Determine the current Minecraft version.
*
* @param server - the Bukkit server that will be used to examine the MC version.
*/
public MinecraftVersion(Server server) {
this(extractVersion(server.getVersion()));
}
/**
* Construct a version object from the format major.minor.build, or the snapshot format.
*
* @param versionOnly - the version in text form.
*/
public MinecraftVersion(String versionOnly) {
this(versionOnly, true);
}
/**
* Construct a version format from the standard release version or the snapshot verison.
*
* @param versionOnly - the version.
* @param parseSnapshot - TRUE to parse the snapshot, FALSE otherwise.
*/
private MinecraftVersion(String versionOnly, boolean parseSnapshot) {
String[] section = versionOnly.split("-");
SnapshotVersion snapshot = null;
int[] numbers = new int[3];
try {
numbers = this.parseVersion(section[0]);
} catch (NumberFormatException cause) {
// Skip snapshot parsing
if (!parseSnapshot) {
throw cause;
}
try {
// Determine if the snapshot is newer than the current release version
snapshot = new SnapshotVersion(section[0]);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
MinecraftVersion latest = new MinecraftVersion(ProtocolLibrary.MAXIMUM_MINECRAFT_VERSION, false);
boolean newer = snapshot.getSnapshotDate().compareTo(
format.parse(ProtocolLibrary.MINECRAFT_LAST_RELEASE_DATE)) > 0;
numbers[0] = latest.getMajor();
numbers[1] = latest.getMinor() + (newer ? 1 : -1);
} catch (Exception e) {
throw new IllegalStateException("Cannot parse " + section[0], e);
}
}
this.major = numbers[0];
this.minor = numbers[1];
this.build = numbers[2];
this.development = section.length > 1 ? section[1] : (snapshot != null ? "snapshot" : null);
this.snapshot = snapshot;
}
/**
* Construct a version object directly.
*
* @param major - major version number.
* @param minor - minor version number.
* @param build - build version number.
*/
public MinecraftVersion(int major, int minor, int build) {
this(major, minor, build, null);
}
/**
* Construct a version object directly.
*
* @param major - major version number.
* @param minor - minor version number.
* @param build - build version number.
* @param development - development stage.
*/
public MinecraftVersion(int major, int minor, int build, String development) {
this.major = major;
this.minor = minor;
this.build = build;
this.development = development;
this.snapshot = null;
}
/**
* Extract the Minecraft version from CraftBukkit itself.
*
* @param text - the server version in text form.
* @return The underlying MC version.
* @throws IllegalStateException If we could not parse the version string.
*/
public static String extractVersion(String text) {
Matcher version = VERSION_PATTERN.matcher(text);
if (version.matches() && version.group(1) != null) {
return version.group(1);
} else {
throw new IllegalStateException("Cannot parse version String '" + text + "'");
}
}
/**
* Parse the given server version into a Minecraft version.
*
* @param serverVersion - the server version.
* @return The resulting Minecraft version.
*/
public static MinecraftVersion fromServerVersion(String serverVersion) {
return new MinecraftVersion(extractVersion(serverVersion));
}
public static MinecraftVersion getCurrentVersion() {
if (currentVersion == null) {
currentVersion = fromServerVersion(Bukkit.getVersion());
}
return currentVersion;
}
public static void setCurrentVersion(MinecraftVersion version) {
currentVersion = version;
}
public static boolean atOrAbove(MinecraftVersion version) {
return getCurrentVersion().isAtLeast(version);
}
private int[] parseVersion(String version) {
String[] elements = version.split("\\.");
int[] numbers = new int[3];
// Make sure it's even a valid version
if (elements.length < 1) {
throw new IllegalStateException("Corrupt MC version: " + version);
}
// The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively.
for (int i = 0; i < Math.min(numbers.length, elements.length); i++) {
numbers[i] = Integer.parseInt(elements[i].trim());
}
return numbers;
}
/**
* Major version number
*
* @return Current major version number.
*/
public int getMajor() {
return this.major;
}
/**
* Minor version number
*
* @return Current minor version number.
*/
public int getMinor() {
return this.minor;
}
/**
* Build version number
*
* @return Current build version number.
*/
public int getBuild() {
return this.build;
}
/**
* Retrieve the development stage.
*
* @return Development stage, or NULL if this is a release.
*/
public String getDevelopmentStage() {
return this.development;
}
/**
* Retrieve the snapshot version, or NULL if this is a release.
*
* @return The snapshot version.
*/
public SnapshotVersion getSnapshot() {
return this.snapshot;
}
/**
* Determine if this version is a snapshot.
*
* @return The snapshot version.
*/
public boolean isSnapshot() {
return this.snapshot != null;
}
/**
* Checks if this version is at or above the current version the server is running.
*
* @return true if this version is equal or newer than the server version, false otherwise.
*/
public boolean atOrAbove() {
if (this.atCurrentOrAbove == null) {
this.atCurrentOrAbove = MinecraftVersion.atOrAbove(this);
}
return this.atCurrentOrAbove;
}
/**
* Retrieve the version String (major.minor.build) only.
*
* @return A normal version string.
*/
public String getVersion() {
if (this.getDevelopmentStage() == null) {
return String.format("%s.%s.%s", this.getMajor(), this.getMinor(), this.getBuild());
} else {
return String.format("%s.%s.%s-%s%s", this.getMajor(), this.getMinor(), this.getBuild(),
this.getDevelopmentStage(), this.isSnapshot() ? this.snapshot : "");
}
}
@Override
public int compareTo(MinecraftVersion o) {
if (o == null) {
return 1;
}
return ComparisonChain.start()
.compare(this.getMajor(), o.getMajor())
.compare(this.getMinor(), o.getMinor())
.compare(this.getBuild(), o.getBuild())
.compare(this.getDevelopmentStage(), o.getDevelopmentStage(), Ordering.natural().nullsLast())
.compare(this.getSnapshot(), o.getSnapshot(), Ordering.natural().nullsFirst())
.result();
}
public boolean isAtLeast(MinecraftVersion other) {
if (other == null) {
return false;
}
return this.compareTo(other) >= 0;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj instanceof MinecraftVersion) {
MinecraftVersion other = (MinecraftVersion) obj;
return this.getMajor() == other.getMajor() &&
this.getMinor() == other.getMinor() &&
this.getBuild() == other.getBuild() &&
Objects.equals(this.getDevelopmentStage(), other.getDevelopmentStage());
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(this.getMajor(), this.getMinor(), this.getBuild());
}
@Override
public String toString() {
// Convert to a String that we can parse back again
return String.format("(MC: %s)", this.getVersion());
}
}
@@ -1,194 +1,194 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 Dan Mulloy
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.google.common.base.Defaults;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Automatically wraps an internal NMS class to a non-versioned, deofbuscated class.
* Requirements:
* <ul>
* <li>The wrapper must be public</li>
* <li>If the wrapper is an internal class, it must be static</li>
* <li>The wrapper must have one public constructor with no arguments (the default constructor is acceptable)</li>
* <li>The wrapper must have the the same number of fields as the NMS class</li>
* <li>Each field should correspond, in order, to its NMS counterpart</li>
* <li>Non-generic fields must have a converter</li>
* </ul>
*
* @author dmulloy2
*/
public class AutoWrapper<T> implements EquivalentConverter<T> {
private static final Object[] NO_ARGS = new Object[0];
private Map<Integer, Function<Object, Object>> wrappers = new HashMap<>();
private Map<Integer, Function<Object, Object>> unwrappers = new HashMap<>();
// lazy
private FieldAccessor[] nmsAccessors;
private FieldAccessor[] wrapperAccessors;
private Object[] nmsDefaultArgs;
private ConstructorAccessor nmsInstanceCreator;
private Class<T> wrapperClass;
private Class<?> nmsClass;
private AutoWrapper(Class<T> wrapperClass, Class<?> nmsClass) {
this.wrapperClass = wrapperClass;
this.nmsClass = nmsClass;
}
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, Class<?> nmsClass) {
return new AutoWrapper<>(wrapperClass, nmsClass);
}
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, String nmsClassName) {
return wrap(wrapperClass, MinecraftReflection.getMinecraftClass(nmsClassName));
}
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, String nmsClassName, String... aliases) {
return wrap(wrapperClass, MinecraftReflection.getMinecraftClass(nmsClassName, aliases));
}
public AutoWrapper<T> field(int index, Function<Object, Object> wrapper, Function<Object, Object> unwrapper) {
wrappers.put(index, wrapper);
unwrappers.put(index, unwrapper);
return this;
}
public AutoWrapper<T> field(int index, EquivalentConverter converter) {
return field(index, converter::getSpecific, specific -> converter.getGeneric(specific));
}
public T wrap(Object nmsObject) {
T instance;
try {
instance = wrapperClass.newInstance();
} catch (ReflectiveOperationException ex) {
throw new InvalidWrapperException(wrapperClass.getSimpleName() + " is not accessible!", ex);
}
// ensures that all accessors are present
computeFieldAccessors();
for (int i = 0; i < wrapperAccessors.length; i++) {
FieldAccessor source = nmsAccessors[i];
FieldAccessor target = wrapperAccessors[i];
Object value = source.get(nmsObject);
if (wrappers.containsKey(i))
value = wrappers.get(i).apply(value);
target.set(instance, value);
}
return instance;
}
public Object unwrap(Object wrapper) {
// ensures that all accessors are present
computeFieldAccessors();
computeNmsConstructorAccess();
Object instance = nmsInstanceCreator.invoke(nmsDefaultArgs);
for (int i = 0; i < wrapperAccessors.length; i++) {
FieldAccessor source = wrapperAccessors[i];
FieldAccessor target = nmsAccessors[i];
Object value = source.get(wrapper);
if (unwrappers.containsKey(i))
value = unwrappers.get(i).apply(value);
target.set(instance, value);
}
return instance;
}
private void computeFieldAccessors() {
if (nmsAccessors == null) {
nmsAccessors = Arrays
.stream(nmsClass.getDeclaredFields())
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.map(field -> Accessors.getFieldAccessor(field))
.toArray(FieldAccessor[]::new);
}
if (wrapperAccessors == null) {
wrapperAccessors = Arrays
.stream(wrapperClass.getDeclaredFields())
.map(field -> Accessors.getFieldAccessor(field))
.toArray(FieldAccessor[]::new);
}
}
private void computeNmsConstructorAccess() {
if (nmsInstanceCreator == null) {
ConstructorAccessor noArgs = Accessors.getConstructorAccessorOrNull(nmsClass);
if (noArgs != null) {
// no args constructor is available - use it
nmsInstanceCreator = noArgs;
nmsDefaultArgs = NO_ARGS;
} else {
// use the first constructor of the class
nmsInstanceCreator = Accessors.getConstructorAccessor(nmsClass.getDeclaredConstructors()[0]);
nmsDefaultArgs = Arrays
.stream(nmsInstanceCreator.getConstructor().getParameterTypes())
.map(type -> type.isPrimitive() ? Defaults.defaultValue(type) : null)
.toArray(Object[]::new);
}
}
}
// ---- Equivalent conversion
@Override
public T getSpecific(Object generic) {
return wrap(generic);
}
@Override
public Object getGeneric(Object specific) {
return unwrap(specific);
}
@Override
public Class<T> getSpecificType() {
return wrapperClass;
}
public static class InvalidWrapperException extends RuntimeException {
private InvalidWrapperException(String message, Throwable cause) {
super(message, cause);
}
}
}
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 Dan Mulloy
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.google.common.base.Defaults;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Automatically wraps an internal NMS class to a non-versioned, deofbuscated class.
* Requirements:
* <ul>
* <li>The wrapper must be public</li>
* <li>If the wrapper is an internal class, it must be static</li>
* <li>The wrapper must have one public constructor with no arguments (the default constructor is acceptable)</li>
* <li>The wrapper must have the the same number of fields as the NMS class</li>
* <li>Each field should correspond, in order, to its NMS counterpart</li>
* <li>Non-generic fields must have a converter</li>
* </ul>
*
* @author dmulloy2
*/
public class AutoWrapper<T> implements EquivalentConverter<T> {
private static final Object[] NO_ARGS = new Object[0];
private Map<Integer, Function<Object, Object>> wrappers = new HashMap<>();
private Map<Integer, Function<Object, Object>> unwrappers = new HashMap<>();
// lazy
private FieldAccessor[] nmsAccessors;
private FieldAccessor[] wrapperAccessors;
private Object[] nmsDefaultArgs;
private ConstructorAccessor nmsInstanceCreator;
private Class<T> wrapperClass;
private Class<?> nmsClass;
private AutoWrapper(Class<T> wrapperClass, Class<?> nmsClass) {
this.wrapperClass = wrapperClass;
this.nmsClass = nmsClass;
}
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, Class<?> nmsClass) {
return new AutoWrapper<>(wrapperClass, nmsClass);
}
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, String nmsClassName) {
return wrap(wrapperClass, MinecraftReflection.getMinecraftClass(nmsClassName));
}
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, String nmsClassName, String... aliases) {
return wrap(wrapperClass, MinecraftReflection.getMinecraftClass(nmsClassName, aliases));
}
public AutoWrapper<T> field(int index, Function<Object, Object> wrapper, Function<Object, Object> unwrapper) {
wrappers.put(index, wrapper);
unwrappers.put(index, unwrapper);
return this;
}
public AutoWrapper<T> field(int index, EquivalentConverter converter) {
return field(index, converter::getSpecific, specific -> converter.getGeneric(specific));
}
public T wrap(Object nmsObject) {
T instance;
try {
instance = wrapperClass.newInstance();
} catch (ReflectiveOperationException ex) {
throw new InvalidWrapperException(wrapperClass.getSimpleName() + " is not accessible!", ex);
}
// ensures that all accessors are present
computeFieldAccessors();
for (int i = 0; i < wrapperAccessors.length; i++) {
FieldAccessor source = nmsAccessors[i];
FieldAccessor target = wrapperAccessors[i];
Object value = source.get(nmsObject);
if (wrappers.containsKey(i))
value = wrappers.get(i).apply(value);
target.set(instance, value);
}
return instance;
}
public Object unwrap(Object wrapper) {
// ensures that all accessors are present
computeFieldAccessors();
computeNmsConstructorAccess();
Object instance = nmsInstanceCreator.invoke(nmsDefaultArgs);
for (int i = 0; i < wrapperAccessors.length; i++) {
FieldAccessor source = wrapperAccessors[i];
FieldAccessor target = nmsAccessors[i];
Object value = source.get(wrapper);
if (unwrappers.containsKey(i))
value = unwrappers.get(i).apply(value);
target.set(instance, value);
}
return instance;
}
private void computeFieldAccessors() {
if (nmsAccessors == null) {
nmsAccessors = Arrays
.stream(nmsClass.getDeclaredFields())
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.map(field -> Accessors.getFieldAccessor(field))
.toArray(FieldAccessor[]::new);
}
if (wrapperAccessors == null) {
wrapperAccessors = Arrays
.stream(wrapperClass.getDeclaredFields())
.map(field -> Accessors.getFieldAccessor(field))
.toArray(FieldAccessor[]::new);
}
}
private void computeNmsConstructorAccess() {
if (nmsInstanceCreator == null) {
ConstructorAccessor noArgs = Accessors.getConstructorAccessorOrNull(nmsClass);
if (noArgs != null) {
// no args constructor is available - use it
nmsInstanceCreator = noArgs;
nmsDefaultArgs = NO_ARGS;
} else {
// use the first constructor of the class
nmsInstanceCreator = Accessors.getConstructorAccessor(nmsClass.getDeclaredConstructors()[0]);
nmsDefaultArgs = Arrays
.stream(nmsInstanceCreator.getConstructor().getParameterTypes())
.map(type -> type.isPrimitive() ? Defaults.defaultValue(type) : null)
.toArray(Object[]::new);
}
}
}
// ---- Equivalent conversion
@Override
public T getSpecific(Object generic) {
return wrap(generic);
}
@Override
public Object getGeneric(Object specific) {
return unwrap(specific);
}
@Override
public Class<T> getSpecificType() {
return wrapperClass;
}
public static class InvalidWrapperException extends RuntimeException {
private InvalidWrapperException(String message, Throwable cause) {
super(message, cause);
}
}
}
@@ -645,6 +645,29 @@ public class BukkitConverters {
}
});
}
/**
* Retrieve a converter for data values in 1.19.3+.
* @return A data value converter.
*/
public static EquivalentConverter<WrappedDataValue> getDataValueConverter() {
return ignoreNull(new EquivalentConverter<WrappedDataValue>() {
@Override
public Object getGeneric(WrappedDataValue specific) {
return specific.getHandle();
}
@Override
public WrappedDataValue getSpecific(Object generic) {
return new WrappedDataValue(generic);
}
@Override
public Class<WrappedDataValue> getSpecificType() {
return WrappedDataValue.class;
}
});
}
/**
* Retrieve a converter for the NMS DataWatcher class and our wrapper.
@@ -1,7 +1,7 @@
package com.comphenix.protocol.wrappers;
public interface ClonableWrapper {
Object getHandle();
ClonableWrapper deepClone();
}
package com.comphenix.protocol.wrappers;
public interface ClonableWrapper {
Object getHandle();
ClonableWrapper deepClone();
}
@@ -1,75 +1,75 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
/**
* Handles component parsing in 1.8
* @author dmulloy2
*/
public class ComponentParser {
private static Constructor readerConstructor;
private static Method setLenient;
private static Method getAdapter;
private static Method read;
private ComponentParser() {
}
public static Object deserialize(Object gson, Class<?> component, StringReader str) {
try {
com.google.gson.stream.JsonReader reader = new com.google.gson.stream.JsonReader(str);
reader.setLenient(true);
return ((com.google.gson.Gson) gson).getAdapter(component).read(reader);
} catch (IOException ex) {
throw new RuntimeException("Failed to read JSON", ex);
} catch (LinkageError er) {
return deserializeLegacy(gson, component, str);
}
}
// Should only be needed on 1.8.
private static Object deserializeLegacy(Object gson, Class<?> component, StringReader str) {
try {
if (readerConstructor == null) {
Class<?> readerClass = Class.forName("org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonReader");
readerConstructor = readerClass.getDeclaredConstructor(Reader.class);
readerConstructor.setAccessible(true);
setLenient = readerClass.getDeclaredMethod("setLenient", boolean.class);
setLenient.setAccessible(true);
getAdapter = gson.getClass().getDeclaredMethod("getAdapter", Class.class);
getAdapter.setAccessible(true);
Object adapter = getAdapter.invoke(gson, component);
read = adapter.getClass().getDeclaredMethod("read", readerClass);
read.setAccessible(true);
}
Object reader = readerConstructor.newInstance(str);
setLenient.invoke(reader, true);
Object adapter = getAdapter.invoke(gson, component);
return read.invoke(adapter, reader);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to read JSON", ex);
}
}
}
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
/**
* Handles component parsing in 1.8
* @author dmulloy2
*/
public class ComponentParser {
private static Constructor readerConstructor;
private static Method setLenient;
private static Method getAdapter;
private static Method read;
private ComponentParser() {
}
public static Object deserialize(Object gson, Class<?> component, StringReader str) {
try {
com.google.gson.stream.JsonReader reader = new com.google.gson.stream.JsonReader(str);
reader.setLenient(true);
return ((com.google.gson.Gson) gson).getAdapter(component).read(reader);
} catch (IOException ex) {
throw new RuntimeException("Failed to read JSON", ex);
} catch (LinkageError er) {
return deserializeLegacy(gson, component, str);
}
}
// Should only be needed on 1.8.
private static Object deserializeLegacy(Object gson, Class<?> component, StringReader str) {
try {
if (readerConstructor == null) {
Class<?> readerClass = Class.forName("org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonReader");
readerConstructor = readerClass.getDeclaredConstructor(Reader.class);
readerConstructor.setAccessible(true);
setLenient = readerClass.getDeclaredMethod("setLenient", boolean.class);
setLenient.setAccessible(true);
getAdapter = gson.getClass().getDeclaredMethod("getAdapter", Class.class);
getAdapter.setAccessible(true);
Object adapter = getAdapter.invoke(gson, component);
read = adapter.getClass().getDeclaredMethod("read", readerClass);
read.setAccessible(true);
}
Object reader = readerConstructor.newInstance(str);
setLenient.invoke(reader, true);
Object adapter = getAdapter.invoke(gson, component);
return read.invoke(adapter, reader);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to read JSON", ex);
}
}
}
@@ -1,174 +1,220 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 Dan Mulloy
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Array;
import java.util.Optional;
import java.util.function.Function;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Utility class for converters
* @author dmulloy2
*/
@SuppressWarnings("unchecked")
public class Converters {
/**
* Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them.
* @param converter Underlying converter
* @param <T> Element type
* @return An ignore null converter
*/
public static <T> EquivalentConverter<T> ignoreNull(final EquivalentConverter<T> converter) {
return new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return generic != null ? converter.getSpecific(generic) : null;
}
@Override
public Object getGeneric(T specific) {
return specific != null ? converter.getGeneric(specific) : null;
}
@Override
public Class<T> getSpecificType() {
return converter.getSpecificType();
}
};
}
/**
* Returns a converter that passes generic and specific values through without converting.
* @param clazz Element class
* @param <T> Element type
* @return A passthrough converter
*/
public static <T> EquivalentConverter<T> passthrough(final Class<T> clazz) {
return ignoreNull(new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return (T) generic;
}
@Override
public Object getGeneric(T specific) {
return specific;
}
@Override
public Class<T> getSpecificType() {
return clazz;
}
});
}
/**
* Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8,
* converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}).
* @param toHandle Function from wrapper to handle (i.e. {@code getHandle()})
* @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)})
* @param <T> Wrapper type
* @return A handle converter
*/
public static <T> EquivalentConverter<T> handle(final Function<T, Object> toHandle,
final Function<Object, T> fromHandle, final Class<T> specificType) {
return new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return fromHandle.apply(generic);
}
@Override
public Object getGeneric(T specific) {
return toHandle.apply(specific);
}
@Override
public Class<T> getSpecificType() {
return specificType;
}
};
}
/**
* Creates a generic array converter. Converts a NMS object array to and from a wrapper array by converting
* each element individually.
*
* @param nmsClass NMS class
* @param converter Underlying converter
* @param <T> Generic type
* @return An array converter
*/
public static <T> EquivalentConverter<T[]> array(final Class<?> nmsClass, final EquivalentConverter<T> converter) {
return new EquivalentConverter<T[]>() {
@Override
public T[] getSpecific(Object generic) {
Object[] array = (Object[]) generic;
Class<T[]> clazz = getSpecificType();
T[] result = clazz.cast(Array.newInstance(clazz.getComponentType(), array.length));
// Unwrap every item
for (int i = 0; i < result.length; i++) {
result[i] = converter.getSpecific(array[i]);
}
return result;
}
@Override
public Object getGeneric(T[] specific) {
Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length);
// Wrap every item
for (int i = 0; i < result.length; i++) {
result[i] = converter.getGeneric(specific[i]);
}
return result;
}
@Override
public Class<T[]> getSpecificType() {
return (Class<T[]>) MinecraftReflection.getArrayClass(converter.getSpecificType());
}
};
}
public static <T> EquivalentConverter<Optional<T>> optional(final EquivalentConverter<T> converter) {
return new EquivalentConverter<Optional<T>>() {
@Override
public Object getGeneric(Optional<T> specific) {
return specific.map(converter::getGeneric);
}
@Override
public Optional<T> getSpecific(Object generic) {
Optional<Object> optional = (Optional<Object>) generic;
return optional.map(converter::getSpecific);
}
@Override
public Class<Optional<T>> getSpecificType() {
return (Class<Optional<T>>) Optional.empty().getClass();
}
};
}
}
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2017 Dan Mulloy
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Function;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Utility class for converters
* @author dmulloy2
*/
@SuppressWarnings("unchecked")
public class Converters {
/**
* Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them.
* @param converter Underlying converter
* @param <T> Element type
* @return An ignore null converter
*/
public static <T> EquivalentConverter<T> ignoreNull(final EquivalentConverter<T> converter) {
return new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return generic != null ? converter.getSpecific(generic) : null;
}
@Override
public Object getGeneric(T specific) {
return specific != null ? converter.getGeneric(specific) : null;
}
@Override
public Class<T> getSpecificType() {
return converter.getSpecificType();
}
};
}
/**
* Returns a converter that passes generic and specific values through without converting.
* @param clazz Element class
* @param <T> Element type
* @return A passthrough converter
*/
public static <T> EquivalentConverter<T> passthrough(final Class<T> clazz) {
return ignoreNull(new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return (T) generic;
}
@Override
public Object getGeneric(T specific) {
return specific;
}
@Override
public Class<T> getSpecificType() {
return clazz;
}
});
}
/**
* Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8,
* converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}).
* @param toHandle Function from wrapper to handle (i.e. {@code getHandle()})
* @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)})
* @param <T> Wrapper type
* @return A handle converter
*/
public static <T> EquivalentConverter<T> handle(final Function<T, Object> toHandle,
final Function<Object, T> fromHandle, final Class<T> specificType) {
return new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return fromHandle.apply(generic);
}
@Override
public Object getGeneric(T specific) {
return toHandle.apply(specific);
}
@Override
public Class<T> getSpecificType() {
return specificType;
}
};
}
/**
* Creates a generic array converter. Converts a NMS object array to and from a wrapper array by converting
* each element individually.
*
* @param nmsClass NMS class
* @param converter Underlying converter
* @param <T> Generic type
* @return An array converter
*/
public static <T> EquivalentConverter<T[]> array(final Class<?> nmsClass, final EquivalentConverter<T> converter) {
return new EquivalentConverter<T[]>() {
@Override
public T[] getSpecific(Object generic) {
Object[] array = (Object[]) generic;
Class<T[]> clazz = getSpecificType();
T[] result = clazz.cast(Array.newInstance(clazz.getComponentType(), array.length));
// Unwrap every item
for (int i = 0; i < result.length; i++) {
result[i] = converter.getSpecific(array[i]);
}
return result;
}
@Override
public Object getGeneric(T[] specific) {
Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length);
// Wrap every item
for (int i = 0; i < result.length; i++) {
result[i] = converter.getGeneric(specific[i]);
}
return result;
}
@Override
public Class<T[]> getSpecificType() {
return (Class<T[]>) MinecraftReflection.getArrayClass(converter.getSpecificType());
}
};
}
public static <T> EquivalentConverter<Optional<T>> optional(final EquivalentConverter<T> converter) {
return new EquivalentConverter<Optional<T>>() {
@Override
public Object getGeneric(Optional<T> specific) {
return specific.map(converter::getGeneric);
}
@Override
public Optional<T> getSpecific(Object generic) {
Optional<Object> optional = (Optional<Object>) generic;
return optional.map(converter::getSpecific);
}
@Override
public Class<Optional<T>> getSpecificType() {
return (Class<Optional<T>>) Optional.empty().getClass();
}
};
}
public static <T, C extends Collection<T>> EquivalentConverter<C> collection(
final EquivalentConverter<T> elementConverter,
final Function<Collection<Object>, C> genericToSpecificCollectionFactory,
final Function<C, Collection<?>> specificToGenericCollectionFactory
) {
return ignoreNull(new EquivalentConverter<C>() {
@Override
public Object getGeneric(C specific) {
// generics are very cool, thank you java
Collection<Object> targetCollection = (Collection<Object>) specificToGenericCollectionFactory.apply(specific);
for (T element : specific) {
Object generic = elementConverter.getGeneric(element);
if (generic != null) {
targetCollection.add(generic);
}
}
return targetCollection;
}
@Override
public C getSpecific(Object generic) {
if (generic instanceof Collection<?>) {
Collection<Object> sourceCollection = (Collection<Object>) generic;
C targetCollection = genericToSpecificCollectionFactory.apply(sourceCollection);
// copy over all elements into a new collection
for (Object element : sourceCollection) {
T specific = elementConverter.getSpecific(element);
if (specific != null) {
targetCollection.add(specific);
}
}
return targetCollection;
}
// not valid
return null;
}
@Override
public Class<C> getSpecificType() {
return (Class) Collection.class;
}
});
}
}
@@ -120,9 +120,15 @@ public abstract class EnumWrappers {
public enum PlayerInfoAction {
ADD_PLAYER,
INITIALIZE_CHAT,
UPDATE_GAME_MODE,
UPDATE_LISTED,
UPDATE_LATENCY,
UPDATE_DISPLAY_NAME,
/**
* @deprecated Removed in 1.19.3
*/
@Deprecated
REMOVE_PLAYER
}
@@ -388,6 +394,7 @@ public abstract class EnumWrappers {
DYING,
CROAKING,
USING_TONGUE,
SITTING,
ROARING,
SNIFFING,
EMERGING,
@@ -490,7 +497,6 @@ public abstract class EnumWrappers {
GAMEMODE_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 0);
RESOURCE_PACK_STATUS_CLASS = getEnum(PacketType.Play.Client.RESOURCE_PACK_STATUS.getPacketClass(), 0);
PLAYER_INFO_ACTION_CLASS = getEnum(PacketType.Play.Server.PLAYER_INFO.getPacketClass(), 0);
TITLE_ACTION_CLASS = getEnum(PacketType.Play.Server.TITLE.getPacketClass(), 0);
WORLD_BORDER_ACTION_CLASS = getEnum(PacketType.Play.Server.WORLD_BORDER.getPacketClass(), 0);
COMBAT_EVENT_TYPE_CLASS = getEnum(PacketType.Play.Server.COMBAT_EVENT.getPacketClass(), 0);
@@ -498,7 +504,18 @@ public abstract class EnumWrappers {
PLAYER_ACTION_CLASS = getEnum(PacketType.Play.Client.ENTITY_ACTION.getPacketClass(), 0);
SCOREBOARD_ACTION_CLASS = getEnum(PacketType.Play.Server.SCOREBOARD_SCORE.getPacketClass(), 0);
PARTICLE_CLASS = getEnum(PacketType.Play.Server.WORLD_PARTICLES.getPacketClass(), 0);
SOUND_CATEGORY_CLASS = getEnum(PacketType.Play.Server.CUSTOM_SOUND_EFFECT.getPacketClass(), 0);
PLAYER_INFO_ACTION_CLASS = getEnum(PacketType.Play.Server.PLAYER_INFO.getPacketClass(), 0);
if (PLAYER_INFO_ACTION_CLASS == null) {
// todo: we can also use getField(0).getGenericType().getTypeParameters()[0]; but this should hold for now
PLAYER_INFO_ACTION_CLASS = PacketType.Play.Server.PLAYER_INFO.getPacketClass().getClasses()[1];
}
try {
SOUND_CATEGORY_CLASS = MinecraftReflection.getMinecraftClass("sounds.SoundCategory");
} catch (Exception ex) {
SOUND_CATEGORY_CLASS = getEnum(PacketType.Play.Server.NAMED_SOUND_EFFECT.getPacketClass(), 0);
}
try {
// TODO enum names are more stable than their packet associations
@@ -798,6 +815,17 @@ public abstract class EnumWrappers {
return new EnumConverter<>(genericClass, specificType);
}
/**
* Creates an enum set with no elements based off the given class. The given must be an enum.
*
* @param clazz the element type of the enum set
* @return a new enum set with the given class as its element type
* @throws ClassCastException if the given class is not an enum
*/
public static <E extends Enum<E>> EnumSet<E> createEmptyEnumSet(Class<?> clazz) {
return EnumSet.noneOf((Class<E>) clazz);
}
/**
* The common Enum converter
*/
@@ -1,103 +1,103 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import org.bukkit.util.Vector;
public class MovingObjectPositionBlock implements Cloneable {
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS(
"world.phys.MovingObjectPositionBlock", "world.phys.BlockHitResult", "MovingObjectPositionBlock");
private BlockPosition position;
private Vector posVector;
private Direction direction;
private boolean insideBlock;
public MovingObjectPositionBlock() { }
public MovingObjectPositionBlock(BlockPosition position, Vector posVector, Direction direction, boolean insideBlock) {
this.position = position;
this.posVector = posVector;
this.direction = direction;
this.insideBlock = insideBlock;
}
public static Class<?> getNmsClass() {
return NMS_CLASS;
}
public BlockPosition getBlockPosition() {
return position;
}
public void setBlockPosition(BlockPosition position) {
this.position = position;
}
public Vector getPosVector() {
return posVector;
}
public void setPosVector(Vector vector) {
this.posVector = vector;
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction direction) {
this.direction = direction;
}
public boolean isInsideBlock() {
return insideBlock;
}
public void setInsideBlock(boolean insideBlock) {
this.insideBlock = insideBlock;
}
private static ConstructorAccessor constructor;
public static EquivalentConverter<MovingObjectPositionBlock> getConverter() {
return Converters.ignoreNull(new EquivalentConverter<MovingObjectPositionBlock>() {
@Override
public Object getGeneric(MovingObjectPositionBlock specific) {
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(NMS_CLASS,
MinecraftReflection.getVec3DClass(),
EnumWrappers.getDirectionClass(),
MinecraftReflection.getBlockPositionClass(),
boolean.class);
}
Object nmsVector = BukkitConverters.getVectorConverter().getGeneric(specific.posVector);
Object nmsDirection = EnumWrappers.getDirectionConverter().getGeneric(specific.direction);
Object nmsBlockPos = BlockPosition.getConverter().getGeneric(specific.position);
return constructor.invoke(nmsVector, nmsDirection, nmsBlockPos, specific.insideBlock);
}
@Override
public MovingObjectPositionBlock getSpecific(Object generic) {
StructureModifier<Object> modifier = new StructureModifier<>(generic.getClass()).withTarget(generic);
Direction direction = modifier.withType(EnumWrappers.getDirectionClass(), EnumWrappers.getDirectionConverter()).read(0);
BlockPosition blockPos = modifier.withType(MinecraftReflection.getBlockPositionClass(), BlockPosition.getConverter()).read(0);
Vector posVector = modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).read(0);
boolean insideBlock = (boolean) modifier.withType(boolean.class).read(1);
return new MovingObjectPositionBlock(blockPos, posVector, direction, insideBlock);
}
@Override
public Class<MovingObjectPositionBlock> getSpecificType() {
return MovingObjectPositionBlock.class;
}
});
}
}
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import org.bukkit.util.Vector;
public class MovingObjectPositionBlock implements Cloneable {
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS(
"world.phys.MovingObjectPositionBlock", "world.phys.BlockHitResult", "MovingObjectPositionBlock");
private BlockPosition position;
private Vector posVector;
private Direction direction;
private boolean insideBlock;
public MovingObjectPositionBlock() { }
public MovingObjectPositionBlock(BlockPosition position, Vector posVector, Direction direction, boolean insideBlock) {
this.position = position;
this.posVector = posVector;
this.direction = direction;
this.insideBlock = insideBlock;
}
public static Class<?> getNmsClass() {
return NMS_CLASS;
}
public BlockPosition getBlockPosition() {
return position;
}
public void setBlockPosition(BlockPosition position) {
this.position = position;
}
public Vector getPosVector() {
return posVector;
}
public void setPosVector(Vector vector) {
this.posVector = vector;
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction direction) {
this.direction = direction;
}
public boolean isInsideBlock() {
return insideBlock;
}
public void setInsideBlock(boolean insideBlock) {
this.insideBlock = insideBlock;
}
private static ConstructorAccessor constructor;
public static EquivalentConverter<MovingObjectPositionBlock> getConverter() {
return Converters.ignoreNull(new EquivalentConverter<MovingObjectPositionBlock>() {
@Override
public Object getGeneric(MovingObjectPositionBlock specific) {
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(NMS_CLASS,
MinecraftReflection.getVec3DClass(),
EnumWrappers.getDirectionClass(),
MinecraftReflection.getBlockPositionClass(),
boolean.class);
}
Object nmsVector = BukkitConverters.getVectorConverter().getGeneric(specific.posVector);
Object nmsDirection = EnumWrappers.getDirectionConverter().getGeneric(specific.direction);
Object nmsBlockPos = BlockPosition.getConverter().getGeneric(specific.position);
return constructor.invoke(nmsVector, nmsDirection, nmsBlockPos, specific.insideBlock);
}
@Override
public MovingObjectPositionBlock getSpecific(Object generic) {
StructureModifier<Object> modifier = new StructureModifier<>(generic.getClass()).withTarget(generic);
Direction direction = modifier.withType(EnumWrappers.getDirectionClass(), EnumWrappers.getDirectionConverter()).read(0);
BlockPosition blockPos = modifier.withType(MinecraftReflection.getBlockPositionClass(), BlockPosition.getConverter()).read(0);
Vector posVector = modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).read(0);
boolean insideBlock = (boolean) modifier.withType(boolean.class).read(1);
return new MovingObjectPositionBlock(blockPos, posVector, direction, insideBlock);
}
@Override
public Class<MovingObjectPositionBlock> getSpecificType() {
return MovingObjectPositionBlock.class;
}
});
}
}
@@ -1,43 +1,43 @@
package com.comphenix.protocol.wrappers;
import java.util.Objects;
public class Pair<A, B> {
private A first;
private B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
public A getFirst() {
return first;
}
public B getSecond() {
return second;
}
public void setFirst(A first) {
this.first = first;
}
public void setSecond(B second) {
this.second = second;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(first, pair.first) &&
Objects.equals(second, pair.second);
}
@Override
public int hashCode() {
return Objects.hash(first, second);
}
}
package com.comphenix.protocol.wrappers;
import java.util.Objects;
public class Pair<A, B> {
private A first;
private B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
public A getFirst() {
return first;
}
public B getSecond() {
return second;
}
public void setFirst(A first) {
this.first = first;
}
public void setSecond(B second) {
this.second = second;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(first, pair.first) &&
Objects.equals(second, pair.second);
}
@Override
public int hashCode() {
return Objects.hash(first, second);
}
}
@@ -21,6 +21,7 @@ import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
@@ -36,7 +37,9 @@ import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
public class PlayerInfoData {
private static Constructor<?> constructor;
private final UUID profileId;
private final int latency;
private final boolean listed;
private final NativeGameMode gameMode;
private final WrappedGameProfile profile;
private final WrappedChatComponent displayName;
@@ -48,11 +51,25 @@ public class PlayerInfoData {
}
public PlayerInfoData(WrappedGameProfile profile, int latency, NativeGameMode gameMode, WrappedChatComponent displayName, WrappedProfileKeyData keyData) {
this.profile = profile;
this(profile.getUUID(), latency, true, gameMode, profile, displayName, keyData);
}
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, WrappedProfileKeyData profileKeyData) {
this.profileId = profileId;
this.latency = latency;
this.listed = listed;
this.gameMode = gameMode;
this.profile = profile;
this.displayName = displayName;
this.profileKeyData = keyData;
this.profileKeyData = profileKeyData;
}
/**
* Get the id of the affected profile (since 1.19.3)
* @return the id of the profile
*/
public UUID getProfileId() {
return profileId;
}
/**
@@ -79,6 +96,14 @@ public class PlayerInfoData {
return latency;
}
/**
* Gets if the player is listed on the client.
* @return if the player is listed
*/
public boolean isListed() {
return listed;
}
/**
* Gets the GameMode of the player represented by this data.
* @return The GameMode
@@ -118,12 +143,23 @@ public class PlayerInfoData {
args.add(PacketType.Play.Server.PLAYER_INFO.getPacketClass());
}
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
args.add(UUID.class);
}
args.add(MinecraftReflection.getGameProfileClass());
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
args.add(boolean.class);
}
args.add(int.class);
args.add(EnumWrappers.getGameModeClass());
args.add(MinecraftReflection.getIChatBaseComponentClass());
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
// RemoteChatSession$a...
args.add(MinecraftReflection.getRemoteChatSessionClass().getClasses()[0]);
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
args.add(MinecraftReflection.getProfilePublicKeyDataClass());
}
@@ -139,7 +175,16 @@ public class PlayerInfoData {
Object gameMode = EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode);
Object displayName = specific.displayName != null ? specific.displayName.handle : null;
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
return constructor.newInstance(
specific.profileId,
specific.profile.handle,
specific.listed,
specific.latency,
gameMode,
displayName,
null); // TODO: do we want to support this?
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
return constructor.newInstance(
specific.profile.handle,
specific.latency,
@@ -178,7 +223,7 @@ public class PlayerInfoData {
WrappedChatComponent displayName = displayNames.read(0);
WrappedProfileKeyData key = null;
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
if (MinecraftVersion.WILD_UPDATE.atOrAbove() && !MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
StructureModifier<WrappedProfileKeyData> keyData = modifier.withType(
MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter());
key = keyData.read(0);
@@ -1,127 +1,127 @@
/**
* (c) 2016 dmulloy2
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import java.lang.reflect.Constructor;
/**
* @author dmulloy2
*/
public class Vector3F {
protected float x;
protected float y;
protected float z;
public Vector3F() {
this(0, 0, 0);
}
public Vector3F(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public float getX() {
return x;
}
public Vector3F setX(float x) {
this.x = x;
return this;
}
public float getY() {
return y;
}
public Vector3F setY(float y) {
this.y = y;
return this;
}
public float getZ() {
return z;
}
public Vector3F setZ(float z) {
this.z = z;
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Float.floatToIntBits(x);
result = prime * result + Float.floatToIntBits(y);
result = prime * result + Float.floatToIntBits(z);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof Vector3F) {
Vector3F that = (Vector3F) obj;
if (Float.floatToIntBits(x) != Float.floatToIntBits(that.x))
return false;
if (Float.floatToIntBits(y) != Float.floatToIntBits(that.y))
return false;
if (Float.floatToIntBits(z) != Float.floatToIntBits(that.z))
return false;
return true;
}
return false;
}
private static Constructor<?> constructor = null;
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS("core.Vector3f", "core.Rotations", "Vector3f");
public static Class<?> getMinecraftClass() {
return NMS_CLASS;
}
public static EquivalentConverter<Vector3F> getConverter() {
return Converters.ignoreNull(new EquivalentConverter<Vector3F>() {
@Override
public Class<Vector3F> getSpecificType() {
return Vector3F.class;
}
@Override
public Object getGeneric(Vector3F specific) {
if (constructor == null) {
try {
constructor = NMS_CLASS.getConstructor(float.class, float.class, float.class);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to find constructor for Vector3f", ex);
}
}
try {
return constructor.newInstance(specific.x, specific.y, specific.z);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to create new instance of Vector3f", ex);
}
}
@Override
public Vector3F getSpecific(Object generic) {
StructureModifier<Float> modifier = new StructureModifier<Float>(generic.getClass())
.withTarget(generic).withType(float.class);
float x = modifier.read(0);
float y = modifier.read(1);
float z = modifier.read(2);
return new Vector3F(x, y, z);
}
});
}
/**
* (c) 2016 dmulloy2
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import java.lang.reflect.Constructor;
/**
* @author dmulloy2
*/
public class Vector3F {
protected float x;
protected float y;
protected float z;
public Vector3F() {
this(0, 0, 0);
}
public Vector3F(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public float getX() {
return x;
}
public Vector3F setX(float x) {
this.x = x;
return this;
}
public float getY() {
return y;
}
public Vector3F setY(float y) {
this.y = y;
return this;
}
public float getZ() {
return z;
}
public Vector3F setZ(float z) {
this.z = z;
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Float.floatToIntBits(x);
result = prime * result + Float.floatToIntBits(y);
result = prime * result + Float.floatToIntBits(z);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof Vector3F) {
Vector3F that = (Vector3F) obj;
if (Float.floatToIntBits(x) != Float.floatToIntBits(that.x))
return false;
if (Float.floatToIntBits(y) != Float.floatToIntBits(that.y))
return false;
if (Float.floatToIntBits(z) != Float.floatToIntBits(that.z))
return false;
return true;
}
return false;
}
private static Constructor<?> constructor = null;
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS("core.Vector3f", "core.Rotations", "Vector3f");
public static Class<?> getMinecraftClass() {
return NMS_CLASS;
}
public static EquivalentConverter<Vector3F> getConverter() {
return Converters.ignoreNull(new EquivalentConverter<Vector3F>() {
@Override
public Class<Vector3F> getSpecificType() {
return Vector3F.class;
}
@Override
public Object getGeneric(Vector3F specific) {
if (constructor == null) {
try {
constructor = NMS_CLASS.getConstructor(float.class, float.class, float.class);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to find constructor for Vector3f", ex);
}
}
try {
return constructor.newInstance(specific.x, specific.y, specific.z);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to create new instance of Vector3f", ex);
}
}
@Override
public Vector3F getSpecific(Object generic) {
StructureModifier<Float> modifier = new StructureModifier<Float>(generic.getClass())
.withTarget(generic).withType(float.class);
float x = modifier.read(0);
float y = modifier.read(1);
float z = modifier.read(2);
return new Vector3F(x, y, z);
}
});
}
}
@@ -0,0 +1,85 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
/**
* Represents a DataValue in 1.19.3+.
*/
public class WrappedDataValue extends AbstractWrapper {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getMinecraftClass("network.syncher.DataWatcher$b");
private static ConstructorAccessor constructor;
private final StructureModifier<Object> modifier;
/**
* Construct a new NMS wrapper.
*
* @param handle the wrapped data value.
*/
public WrappedDataValue(Object handle) {
super(HANDLE_TYPE);
this.setHandle(handle);
this.modifier = new StructureModifier<>(this.handleType).withTarget(handle);
}
public WrappedDataValue(int index, Serializer serializer, Object value) {
this(newHandle(index, serializer, value));
}
private static Object newHandle(int index, Serializer serializer, Object value) {
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
}
return constructor.invoke(index, serializer.getHandle(), value);
}
public int getIndex() {
return this.modifier.<Integer>withType(int.class).read(0);
}
public void setIndex(int index) {
this.modifier.withType(int.class).write(0, index);
}
public Serializer getSerializer() {
Object serializer = this.modifier.readSafely(1);
if (serializer != null) {
Serializer wrapper = Registry.fromHandle(serializer);
if (wrapper != null) {
return wrapper;
} else {
return new Serializer(null, serializer, false);
}
} else {
return null;
}
}
public void setSerializer(Serializer serializer) {
this.modifier.writeSafely(1, serializer == null ? null : serializer.getHandle());
}
public Object getValue() {
return WrappedWatchableObject.getWrapped(getRawValue());
}
public void setValue(Object value) {
setRawValue(WrappedWatchableObject.getUnwrapped(value));
}
public Object getRawValue() {
return this.modifier.readSafely(2);
}
public void setRawValue(Object value) {
this.modifier.writeSafely(2, value);
}
}
@@ -132,7 +132,7 @@ public final class WrappedLevelChunkData {
* @return a newly created wrapper
*/
public static ChunkData fromValues(NbtCompound heightmapsTag, byte[] buffer, List<BlockEntityInfo> blockEntityInfo) {
ChunkData data = new ChunkData(LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR.invoke(StructureCache.getTrickDataSerializerOrNull(), 0, 0));
ChunkData data = new ChunkData(LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR.invoke(StructureCache.newNullDataSerializer(), 0, 0));
data.setHeightmapsTag(heightmapsTag);
data.setBuffer(buffer);
@@ -1,169 +1,183 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Modifier;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.mojang.math.Vector3fa;
import org.bukkit.Color;
import org.bukkit.Particle;
import org.bukkit.inventory.ItemStack;
/**
* Represents an immutable wrapped ParticleParam in 1.13
*/
public class WrappedParticle<T> {
private static MethodAccessor toBukkit;
private static MethodAccessor toNMS;
private static MethodAccessor toCraftData;
private static void ensureMethods() {
if (toBukkit != null && toNMS != null) {
return;
}
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle"));
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(Particle.class)
.parameterExactType(MinecraftReflection.getParticleParam())
.build();
toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(MinecraftReflection.getParticleParam())
.parameterCount(2)
.build();
toNMS = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
Class<?> cbData = MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData");
fuzzy = FuzzyReflection.fromClass(cbData);
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(cbData)
.parameterExactArray(MinecraftReflection.getIBlockDataClass())
.build();
toCraftData = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
}
private final Particle particle;
private final T data;
private final Object handle;
private WrappedParticle(Object handle, Particle particle, T data) {
this.handle = handle;
this.particle = particle;
this.data = data;
}
/**
* @return This particle's Bukkit type
*/
public Particle getParticle() {
return particle;
}
/**
* Gets this Particle's Bukkit/ProtocolLib data. The type of this data depends on the
* {@link #getParticle() Particle type}. For Block particles it will be {@link WrappedBlockData},
* for Item crack particles, it will be an {@link ItemStack}, and for redstone particles it will
* be {@link Particle.DustOptions}
*
* @return The particle data
*/
public T getData() {
return data;
}
/**
* @return NMS handle
*/
public Object getHandle() {
return handle;
}
public static WrappedParticle fromHandle(Object handle) {
ensureMethods();
Particle bukkit = (Particle) toBukkit.invoke(null, handle);
Object data = null;
switch (bukkit) {
case BLOCK_CRACK:
case BLOCK_DUST:
case FALLING_DUST:
data = getBlockData(handle);
break;
case ITEM_CRACK:
data = getItem(handle);
break;
case REDSTONE:
data = getRedstone(handle);
break;
default:
break;
}
return new WrappedParticle<>(handle, bukkit, data);
}
private static WrappedBlockData getBlockData(Object handle) {
return new StructureModifier<>(handle.getClass())
.withTarget(handle)
.withType(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter())
.read(0);
}
private static Object getItem(Object handle) {
return new StructureModifier<>(handle.getClass())
.withTarget(handle)
.withType(MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter())
.read(0);
}
private static Object getRedstone(Object handle) {
int r, g, b;
float alpha;
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
Vector3fa rgb = (Vector3fa) modifier.withType(Vector3fa.class).read(0);
r = (int) (rgb.a() * 255);
g = (int) (rgb.b() * 255);
b = (int) (rgb.c() * 255);
alpha = (float) modifier.withType(float.class).read(0);
} else {
StructureModifier<Float> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class);
r = (int) (modifier.read(0) * 255);
g = (int) (modifier.read(1) * 255);
b = (int) (modifier.read(2) * 255);
alpha = modifier.read(3);
}
return new Particle.DustOptions(Color.fromRGB(r, g, b), alpha);
}
public static <T> WrappedParticle<T> create(Particle particle, T data) {
ensureMethods();
Object bukkitData = data;
if (data instanceof WrappedBlockData) {
WrappedBlockData blockData = (WrappedBlockData) data;
bukkitData = toCraftData.invoke(null, blockData.getHandle());
}
Object handle = toNMS.invoke(null, particle, bukkitData);
return new WrappedParticle<>(handle, particle, data);
}
}
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Modifier;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import org.bukkit.Color;
import org.bukkit.Particle;
import org.bukkit.inventory.ItemStack;
/**
* Represents an immutable wrapped ParticleParam in 1.13
*/
public class WrappedParticle<T> {
private static Class<?> VECTOR_3FA;
private static MethodAccessor toBukkit;
private static MethodAccessor toNMS;
private static MethodAccessor toCraftData;
private static void ensureMethods() {
if (toBukkit != null && toNMS != null) {
return;
}
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle"));
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(Particle.class)
.parameterExactType(MinecraftReflection.getParticleParam())
.build();
toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(MinecraftReflection.getParticleParam())
.parameterCount(2)
.build();
toNMS = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
Class<?> cbData = MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData");
fuzzy = FuzzyReflection.fromClass(cbData);
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(cbData)
.parameterExactArray(MinecraftReflection.getIBlockDataClass())
.build();
toCraftData = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
}
private final Particle particle;
private final T data;
private final Object handle;
private WrappedParticle(Object handle, Particle particle, T data) {
this.handle = handle;
this.particle = particle;
this.data = data;
}
/**
* @return This particle's Bukkit type
*/
public Particle getParticle() {
return particle;
}
/**
* Gets this Particle's Bukkit/ProtocolLib data. The type of this data depends on the
* {@link #getParticle() Particle type}. For Block particles it will be {@link WrappedBlockData},
* for Item crack particles, it will be an {@link ItemStack}, and for redstone particles it will
* be {@link Particle.DustOptions}
*
* @return The particle data
*/
public T getData() {
return data;
}
/**
* @return NMS handle
*/
public Object getHandle() {
return handle;
}
public static WrappedParticle fromHandle(Object handle) {
ensureMethods();
Particle bukkit = (Particle) toBukkit.invoke(null, handle);
Object data = null;
switch (bukkit) {
case BLOCK_CRACK:
case BLOCK_DUST:
case FALLING_DUST:
data = getBlockData(handle);
break;
case ITEM_CRACK:
data = getItem(handle);
break;
case REDSTONE:
data = getRedstone(handle);
break;
default:
break;
}
return new WrappedParticle<>(handle, bukkit, data);
}
private static WrappedBlockData getBlockData(Object handle) {
return new StructureModifier<>(handle.getClass())
.withTarget(handle)
.withType(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter())
.read(0);
}
private static Object getItem(Object handle) {
return new StructureModifier<>(handle.getClass())
.withTarget(handle)
.withType(MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter())
.read(0);
}
private static Object getRedstone(Object handle) {
int r, g, b;
float alpha;
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
org.joml.Vector3f rgb = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.class).read(0);
r = (int) (rgb.x() * 255);
g = (int) (rgb.y() * 255);
b = (int) (rgb.z() * 255);
alpha = (float) modifier.withType(float.class).read(0);
} else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
if (VECTOR_3FA == null) {
VECTOR_3FA = MinecraftReflection.getLibraryClass("com.mojang.math.Vector3fa");
}
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
Object rgb = modifier.withType(VECTOR_3FA).read(0);
StructureModifier<Object> rgbModifier = new StructureModifier<>(VECTOR_3FA).withTarget(rgb);
r = (int) (rgbModifier.<Float>withType(float.class).read(0) * 255);
g = (int) (rgbModifier.<Float>withType(float.class).read(1) * 255);
b = (int) (rgbModifier.<Float>withType(float.class).read(2) * 255);
alpha = (float) modifier.withType(float.class).read(0);
} else {
StructureModifier<Float> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class);
r = (int) (modifier.read(0) * 255);
g = (int) (modifier.read(1) * 255);
b = (int) (modifier.read(2) * 255);
alpha = modifier.read(3);
}
return new Particle.DustOptions(Color.fromRGB(r, g, b), alpha);
}
public static <T> WrappedParticle<T> create(Particle particle, T data) {
ensureMethods();
Object bukkitData = data;
if (data instanceof WrappedBlockData) {
WrappedBlockData blockData = (WrappedBlockData) data;
bukkitData = toCraftData.invoke(null, blockData.getHandle());
}
Object handle = toNMS.invoke(null, particle, bukkitData);
return new WrappedParticle<>(handle, particle, data);
}
}
@@ -14,6 +14,7 @@ import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class WrappedRegistry {
// map of NMS class to registry instance
@@ -26,12 +27,18 @@ public class WrappedRegistry {
static {
Map<Class<?>, WrappedRegistry> regMap = new HashMap<>();
Class<?> regClass = MinecraftReflection.getIRegistry();
if (regClass != null) {
for (Field field : regClass.getFields()) {
// get the possible registry fields
Class<?> iRegistry = MinecraftReflection.getIRegistry();
Class<?> builtInRegistries = MinecraftReflection.getBuiltInRegistries();
Set<Field> registries = FuzzyReflection.combineArrays(
iRegistry == null ? null : iRegistry.getFields(),
builtInRegistries == null ? null : builtInRegistries.getFields());
if (iRegistry != null && !registries.isEmpty()) {
for (Field field : registries) {
try {
// make sure it's actually a registry
if (regClass.isAssignableFrom(field.getType())) {
if (iRegistry.isAssignableFrom(field.getType())) {
Type genType = field.getGenericType();
if (genType instanceof ParameterizedType) {
ParameterizedType par = (ParameterizedType) genType;
@@ -73,7 +80,7 @@ public class WrappedRegistry {
REGISTRY = ImmutableMap.copyOf(regMap);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(regClass, false);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(iRegistry, false);
GET = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
@@ -183,7 +183,9 @@ public class WrappedServerPing extends AbstractWrapper implements ClonableWrappe
* Retrieve whether chat preview is enabled on the server.
* @return whether chat preview is enabled on the server.
* @since 1.19
* @deprecated Removed in 1.19.3
*/
@Deprecated
public boolean isChatPreviewEnabled() {
return (Boolean) BOOLEAN_ACCESSORS[0].get(handle);
}
@@ -192,7 +194,9 @@ public class WrappedServerPing extends AbstractWrapper implements ClonableWrappe
* Sets whether chat preview is enabled on the server.
* @param chatPreviewEnabled true if enabled, false otherwise.
* @since 1.19
* @deprecated Removed in 1.19.3
*/
@Deprecated
public void setChatPreviewEnabled(boolean chatPreviewEnabled) {
BOOLEAN_ACCESSORS[0].set(handle, chatPreviewEnabled);
}
@@ -203,7 +207,8 @@ public class WrappedServerPing extends AbstractWrapper implements ClonableWrappe
* @since 1.19.1
*/
public boolean isEnforceSecureChat() {
return (Boolean) BOOLEAN_ACCESSORS[1].get(handle);
int index = MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove() ? 0 : 1;
return (Boolean) BOOLEAN_ACCESSORS[index].get(handle);
}
/**
@@ -212,7 +217,8 @@ public class WrappedServerPing extends AbstractWrapper implements ClonableWrappe
* @since 1.19.1
*/
public void setEnforceSecureChat(boolean enforceSecureChat) {
BOOLEAN_ACCESSORS[1].set(handle, enforceSecureChat);
int index = MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove() ? 0 : 1;
BOOLEAN_ACCESSORS[index].set(handle, enforceSecureChat);
}
/**
@@ -1,84 +1,84 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
public class WrappedVillagerData extends AbstractWrapper implements ClonableWrapper {
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS(
"world.entity.npc.VillagerData","VillagerData");
private static final Class<?> TYPE_CLASS = MinecraftReflection.getNullableNMS(
"world.entity.npc.VillagerType", "VillagerType");
private static final Class<?> PROF_CLASS = MinecraftReflection.getNullableNMS(
"world.entity.npc.VillagerProfession", "VillagerProfession");
private static EquivalentConverter<Type> TYPE_CONVERTER;
private static EquivalentConverter<Profession> PROF_CONVERTER;
static {
if (NMS_CLASS != null) {
TYPE_CONVERTER = new EnumWrappers.FauxEnumConverter<>(Type.class, TYPE_CLASS);
PROF_CONVERTER = new EnumWrappers.FauxEnumConverter<>(Profession.class, PROF_CLASS);
}
}
public enum Type {
DESERT, JUNGLE, PLAINS, SAVANNA, SNOW, SWAMP, TAIGA
}
public enum Profession {
NONE, ARMORER, BUTCHER, CARTOGRAPHER, CLERIC, FARMER, FISHERMAN,
FLETCHER, LEATHERWORKER, LIBRARIAN, MASON, NITWIT, SHEPHERD,
TOOLSMITH, WEAPONSMITH
}
private StructureModifier<Object> modifier;
private WrappedVillagerData(Object handle) {
super(NMS_CLASS);
setHandle(handle);
modifier = new StructureModifier<>(NMS_CLASS).withTarget(handle);
}
public static WrappedVillagerData fromHandle(Object handle) {
return new WrappedVillagerData(handle);
}
private static ConstructorAccessor CONSTRUCTOR;
public static WrappedVillagerData fromValues(Type type, Profession profession, int level) {
Object genericType = TYPE_CONVERTER.getGeneric(type);
Object genericProf = PROF_CONVERTER.getGeneric(profession);
if (CONSTRUCTOR == null) {
CONSTRUCTOR = Accessors.getConstructorAccessor(NMS_CLASS, TYPE_CLASS, PROF_CLASS, int.class);
}
Object handle = CONSTRUCTOR.invoke(genericType, genericProf, level);
return fromHandle(handle);
}
public static Class<?> getNmsClass() {
return NMS_CLASS;
}
public int getLevel() {
return modifier.<Integer>withType(int.class).read(0);
}
public Type getType() {
return modifier.withType(TYPE_CLASS, TYPE_CONVERTER).read(0);
}
public Profession getProfession() {
return modifier.withType(PROF_CLASS, PROF_CONVERTER).read(0);
}
@Override
public WrappedVillagerData deepClone() {
return WrappedVillagerData.fromValues(getType(), getProfession(), getLevel());
}
}
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
public class WrappedVillagerData extends AbstractWrapper implements ClonableWrapper {
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS(
"world.entity.npc.VillagerData","VillagerData");
private static final Class<?> TYPE_CLASS = MinecraftReflection.getNullableNMS(
"world.entity.npc.VillagerType", "VillagerType");
private static final Class<?> PROF_CLASS = MinecraftReflection.getNullableNMS(
"world.entity.npc.VillagerProfession", "VillagerProfession");
private static EquivalentConverter<Type> TYPE_CONVERTER;
private static EquivalentConverter<Profession> PROF_CONVERTER;
static {
if (NMS_CLASS != null) {
TYPE_CONVERTER = new EnumWrappers.FauxEnumConverter<>(Type.class, TYPE_CLASS);
PROF_CONVERTER = new EnumWrappers.FauxEnumConverter<>(Profession.class, PROF_CLASS);
}
}
public enum Type {
DESERT, JUNGLE, PLAINS, SAVANNA, SNOW, SWAMP, TAIGA
}
public enum Profession {
NONE, ARMORER, BUTCHER, CARTOGRAPHER, CLERIC, FARMER, FISHERMAN,
FLETCHER, LEATHERWORKER, LIBRARIAN, MASON, NITWIT, SHEPHERD,
TOOLSMITH, WEAPONSMITH
}
private StructureModifier<Object> modifier;
private WrappedVillagerData(Object handle) {
super(NMS_CLASS);
setHandle(handle);
modifier = new StructureModifier<>(NMS_CLASS).withTarget(handle);
}
public static WrappedVillagerData fromHandle(Object handle) {
return new WrappedVillagerData(handle);
}
private static ConstructorAccessor CONSTRUCTOR;
public static WrappedVillagerData fromValues(Type type, Profession profession, int level) {
Object genericType = TYPE_CONVERTER.getGeneric(type);
Object genericProf = PROF_CONVERTER.getGeneric(profession);
if (CONSTRUCTOR == null) {
CONSTRUCTOR = Accessors.getConstructorAccessor(NMS_CLASS, TYPE_CLASS, PROF_CLASS, int.class);
}
Object handle = CONSTRUCTOR.invoke(genericType, genericProf, level);
return fromHandle(handle);
}
public static Class<?> getNmsClass() {
return NMS_CLASS;
}
public int getLevel() {
return modifier.<Integer>withType(int.class).read(0);
}
public Type getType() {
return modifier.withType(TYPE_CLASS, TYPE_CONVERTER).read(0);
}
public Profession getProfession() {
return modifier.withType(PROF_CLASS, PROF_CONVERTER).read(0);
}
@Override
public WrappedVillagerData deepClone() {
return WrappedVillagerData.fromValues(getType(), getProfession(), getLevel());
}
}
+44 -44
View File
@@ -1,45 +1,45 @@
name: ProtocolLib
version: ${project.fullVersion}
description: Provides read/write access to the Minecraft protocol.
authors: [dmulloy2, comphenix]
main: com.comphenix.protocol.ProtocolLib
load: STARTUP
database: false
api-version: "1.13"
commands:
protocol:
description: Performs administrative tasks regarding ProtocolLib.
usage: /<command> config|check|update|timings|listeners|version|dump
permission: protocol.admin
permission-message: You don't have <permission>
packet:
description: Add or remove a simple packet listener.
usage: /<command> add|remove|names client|server [ID start]-[ID stop] [detailed]
permission: protocol.admin
permission-message: You don't have <permission>
filter:
description: Add or remove programmable filters to the packet listeners.
usage: /<command> add|remove name [ID start]-[ID stop]
aliases: [packet_filter]
permission: protocol.admin
permission-message: You don't have <permission>
packetlog:
description: Logs hex representations of packets to a file or console
usage: /<command> <protocol> <sender> <packet> [location]
permission: protocol.admin
permission-message: You don't have <permission>
permissions:
protocol.*:
description: Gives access to everything.
children:
protocol.admin: true
protocol.info: true
protocol.admin:
description: Able to initiate the update process, and can configure debug mode.
default: op
protocol.info:
description: Can read update notifications and error reports.
name: ProtocolLib
version: ${project.fullVersion}
description: Provides read/write access to the Minecraft protocol.
authors: [dmulloy2, comphenix]
main: com.comphenix.protocol.ProtocolLib
load: STARTUP
database: false
api-version: "1.13"
commands:
protocol:
description: Performs administrative tasks regarding ProtocolLib.
usage: /<command> config|check|update|timings|listeners|version|dump
permission: protocol.admin
permission-message: You don't have <permission>
packet:
description: Add or remove a simple packet listener.
usage: /<command> add|remove|names client|server [ID start]-[ID stop] [detailed]
permission: protocol.admin
permission-message: You don't have <permission>
filter:
description: Add or remove programmable filters to the packet listeners.
usage: /<command> add|remove name [ID start]-[ID stop]
aliases: [packet_filter]
permission: protocol.admin
permission-message: You don't have <permission>
packetlog:
description: Logs hex representations of packets to a file or console
usage: /<command> <protocol> <sender> <packet> [location]
permission: protocol.admin
permission-message: You don't have <permission>
permissions:
protocol.*:
description: Gives access to everything.
children:
protocol.admin: true
protocol.info: true
protocol.admin:
description: Able to initiate the update process, and can configure debug mode.
default: op
protocol.info:
description: Can read update notifications and error reports.
default: op
@@ -1,126 +1,127 @@
package com.comphenix.protocol;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
import java.util.Collections;
import java.util.List;
import net.minecraft.SharedConstants;
import net.minecraft.core.IRegistry;
import net.minecraft.server.DispenserRegistry;
import net.minecraft.server.level.WorldServer;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_19_R1.util.Versioning;
import org.spigotmc.SpigotWorldConfig;
/**
* Used to ensure that ProtocolLib and Bukkit is prepared to be tested.
*
* @author Kristian
*/
public class BukkitInitialization {
private static final BukkitInitialization instance = new BukkitInitialization();
private boolean initialized;
private boolean packaged;
private BukkitInitialization() {
System.out.println("Created new BukkitInitialization on " + Thread.currentThread().getName());
}
/**
* Statically initializes the mock server for unit testing
*/
public static synchronized void initializeAll() {
instance.initialize();
}
/**
* Initialize Bukkit and ProtocolLib such that we can perfrom unit testing
*/
private void initialize() {
if (!this.initialized) {
// Denote that we're done
this.initialized = true;
try {
LogManager.getLogger();
} catch (Throwable ex) {
// Happens only on my Jenkins, but if it errors here it works when it matters
ex.printStackTrace();
}
instance.setPackage();
SharedConstants.a();
DispenserRegistry.a();
try {
IRegistry.class.getName();
} catch (Throwable ex) {
ex.printStackTrace();
}
String releaseTarget = SharedConstants.b().getReleaseTarget();
String serverVersion = CraftServer.class.getPackage().getImplementationVersion();
// Mock the server object
Server mockedServer = mock(Server.class);
when(mockedServer.getLogger()).thenReturn(java.util.logging.Logger.getLogger("Minecraft"));
when(mockedServer.getName()).thenReturn("Mock Server");
when(mockedServer.getVersion()).thenReturn(serverVersion + " (MC: " + releaseTarget + ")");
when(mockedServer.getBukkitVersion()).thenReturn(Versioning.getBukkitVersion());
when(mockedServer.getItemFactory()).thenReturn(CraftItemFactory.instance());
when(mockedServer.isPrimaryThread()).thenReturn(true);
WorldServer nmsWorld = mock(WorldServer.class);
SpigotWorldConfig mockWorldConfig = mock(SpigotWorldConfig.class);
try {
FieldAccessor spigotConfig = Accessors.getFieldAccessor(nmsWorld.getClass().getField("spigotConfig"));
spigotConfig.set(nmsWorld, mockWorldConfig);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
CraftWorld world = mock(CraftWorld.class);
when(world.getHandle()).thenReturn(nmsWorld);
List<World> worlds = Collections.singletonList(world);
when(mockedServer.getWorlds()).thenReturn(worlds);
// Inject this fake server
Bukkit.setServer(mockedServer);
}
}
/**
* Ensure that package names are correctly set up.
*/
private void setPackage() {
if (!this.packaged) {
this.packaged = true;
try {
LogManager.getLogger();
} catch (Throwable ex) {
// Happens only on my Jenkins, but if it errors here it works when it matters
ex.printStackTrace();
}
MinecraftReflectionTestUtil.init();
}
}
}
package com.comphenix.protocol;
import java.util.Collections;
import java.util.List;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
import net.minecraft.SharedConstants;
import net.minecraft.core.IRegistry;
import net.minecraft.server.DispenserRegistry;
import net.minecraft.server.level.WorldServer;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_19_R2.CraftServer;
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_19_R2.util.Versioning;
import org.spigotmc.SpigotWorldConfig;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Used to ensure that ProtocolLib and Bukkit is prepared to be tested.
*
* @author Kristian
*/
public class BukkitInitialization {
private static final BukkitInitialization instance = new BukkitInitialization();
private boolean initialized;
private boolean packaged;
private BukkitInitialization() {
System.out.println("Created new BukkitInitialization on " + Thread.currentThread().getName());
}
/**
* Statically initializes the mock server for unit testing
*/
public static synchronized void initializeAll() {
instance.initialize();
}
/**
* Initialize Bukkit and ProtocolLib such that we can perfrom unit testing
*/
private void initialize() {
if (!this.initialized) {
// Denote that we're done
this.initialized = true;
try {
LogManager.getLogger();
} catch (Throwable ex) {
// Happens only on my Jenkins, but if it errors here it works when it matters
ex.printStackTrace();
}
instance.setPackage();
SharedConstants.a();
DispenserRegistry.a();
try {
IRegistry.class.getName();
} catch (Throwable ex) {
ex.printStackTrace();
}
String releaseTarget = MinecraftReflectionTestUtil.RELEASE_TARGET;
String serverVersion = CraftServer.class.getPackage().getImplementationVersion();
// Mock the server object
Server mockedServer = mock(Server.class);
when(mockedServer.getLogger()).thenReturn(java.util.logging.Logger.getLogger("Minecraft"));
when(mockedServer.getName()).thenReturn("Mock Server");
when(mockedServer.getVersion()).thenReturn(serverVersion + " (MC: " + releaseTarget + ")");
when(mockedServer.getBukkitVersion()).thenReturn(Versioning.getBukkitVersion());
when(mockedServer.getItemFactory()).thenReturn(CraftItemFactory.instance());
when(mockedServer.isPrimaryThread()).thenReturn(true);
WorldServer nmsWorld = mock(WorldServer.class);
SpigotWorldConfig mockWorldConfig = mock(SpigotWorldConfig.class);
try {
FieldAccessor spigotConfig = Accessors.getFieldAccessor(nmsWorld.getClass().getField("spigotConfig"));
spigotConfig.set(nmsWorld, mockWorldConfig);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
CraftWorld world = mock(CraftWorld.class);
when(world.getHandle()).thenReturn(nmsWorld);
List<World> worlds = Collections.singletonList(world);
when(mockedServer.getWorlds()).thenReturn(worlds);
// Inject this fake server
Bukkit.setServer(mockedServer);
}
}
/**
* Ensure that package names are correctly set up.
*/
private void setPackage() {
if (!this.packaged) {
this.packaged = true;
try {
LogManager.getLogger();
} catch (Throwable ex) {
// Happens only on my Jenkins, but if it errors here it works when it matters
ex.printStackTrace();
}
MinecraftReflectionTestUtil.init();
}
}
}
@@ -1,334 +1,334 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2016 dmulloy2
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.comphenix.protocol;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import net.minecraft.network.EnumProtocol;
import net.minecraft.network.protocol.EnumProtocolDirection;
import net.minecraft.network.protocol.login.PacketLoginInStart;
import org.apache.commons.lang.WordUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author dmulloy2
*/
public class PacketTypeTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
// I'm well aware this is jank, but it does in fact work correctly and give the desired result
PacketType.onDynamicCreate = className -> {
throw new RuntimeException("Dynamically generated packet " + className);
};
}
@AfterAll
public static void afterClass() {
PacketType.onDynamicCreate = __ -> {
};
}
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
MinecraftReflectionTestUtil.init();
Set<Class<?>> allTypes = new HashSet<>();
List<Class<?>> newTypes = new ArrayList<>();
EnumProtocol[] protocols = EnumProtocol.values();
for (EnumProtocol protocol : protocols) {
System.out.println(WordUtils.capitalize(protocol.name().toLowerCase()));
Field field = EnumProtocol.class.getDeclaredField("j");
field.setAccessible(true);
Map<EnumProtocolDirection, Object> map = (Map<EnumProtocolDirection, Object>) field.get(protocol);
for (Entry<EnumProtocolDirection, Object> entry : map.entrySet()) {
Field mapField = entry.getValue().getClass().getDeclaredField("b");
mapField.setAccessible(true);
Map<Class<?>, Integer> reverseMap = (Map<Class<?>, Integer>) mapField.get(entry.getValue());
Map<Integer, Class<?>> treeMap = new TreeMap<>();
for (Entry<Class<?>, Integer> entry1 : reverseMap.entrySet()) {
treeMap.put(entry1.getValue(), entry1.getKey());
}
System.out.println(" " + entry.getKey());
for (Entry<Integer, Class<?>> entry1 : treeMap.entrySet()) {
System.out.println(generateNewType(entry1.getKey(), entry1.getValue()));
allTypes.add(entry1.getValue());
try {
PacketType.fromClass(entry1.getValue());
} catch (Exception ex) {
newTypes.add(entry1.getValue());
}
}
}
}
System.out.println("New types: " + newTypes);
for (PacketType type : PacketType.values()) {
if (type.isDeprecated()) {
continue;
}
if (!allTypes.contains(type.getPacketClass())) {
System.out.println(type + " was removed");
}
}
}
private static String formatHex(int dec) {
if (dec < 0) {
return "0xFF";
}
String hex = Integer.toHexString(dec).toUpperCase();
return "0x" + (hex.length() < 2 ? "0" : "") + hex;
}
private static List<String> splitOnCaps(String string) {
List<String> list = new ArrayList<>();
StringBuilder builder = new StringBuilder();
char[] chars = string.toCharArray();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
if (i != 0 && Character.isUpperCase(c)) {
list.add(builder.toString());
builder = new StringBuilder();
}
builder.append(c);
}
list.add(builder.toString());
return list;
}
private static String generateNewType(int packetId, Class<?> clazz) {
StringBuilder builder = new StringBuilder();
builder.append("\t\t\t");
builder.append("public static final PacketType ");
String fullName = clazz.getName();
fullName = fullName.substring(fullName.lastIndexOf(".") + 1);
String className;
List<String> classNames = new ArrayList<>();
if (fullName.endsWith("Packet")) {
for (String name : fullName.split("\\$")) {
List<String> split = splitOnCaps(name);
StringBuilder nameBuilder = new StringBuilder();
for (int i = 1; i < split.size() - 1; i++) {
nameBuilder.append(split.get(i));
}
classNames.add(nameBuilder.toString());
}
} else {
for (String name : fullName.split("\\$")) {
List<String> split = splitOnCaps(name);
StringBuilder nameBuilder = new StringBuilder();
for (int i = 3; i < split.size(); i++) {
nameBuilder.append(split.get(i));
}
classNames.add(nameBuilder.toString());
}
}
className = classNames.get(classNames.size() - 1);
// Format it like SET_PROTOCOL
StringBuilder fieldName = new StringBuilder();
char[] chars = className.toCharArray();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
if (i != 0 && Character.isUpperCase(c)) {
fieldName.append("_");
}
fieldName.append(Character.toUpperCase(c));
}
builder.append(fieldName.toString().replace("N_B_T", "NBT"));
builder.append(" = ");
// Add spacing
if (builder.length() > 65) {
builder.append("\n");
} else {
while (builder.length() < 65) {
builder.append(" ");
}
}
builder.append("new ");
builder.append("PacketType(PROTOCOL, SENDER, ");
builder.append(formatHex(packetId));
builder.append(", ");
StringBuilder nameBuilder = new StringBuilder();
for (int i = 0; i < classNames.size(); i++) {
if (i != 0) {
nameBuilder.append("$");
}
nameBuilder.append(classNames.get(i));
}
String name = nameBuilder.toString();
String namesArg = listToString(getAllNames(clazz, name));
builder.append(namesArg);
builder.append(");");
return builder.toString();
}
private static List<String> getAllNames(Class<?> packetClass, String newName) {
List<String> names = new ArrayList<>();
names.add(newName);
try {
PacketType type = PacketType.fromClass(packetClass);
for (String alias : type.names) {
alias = alias.substring(alias.lastIndexOf('.') + 1);
if (!names.contains(alias)) {
names.add(alias);
}
}
} catch (Exception ignored) {
}
return names;
}
private static String listToString(List<String> list) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
if (i != 0) {
builder.append(", ");
}
builder.append("\"").append(list.get(i)).append("\"");
}
return builder.toString();
}
@BeforeAll
public static void initializeReflection() {
BukkitInitialization.initializeAll();
}
@Test
public void testFindCurrent() {
assertEquals(PacketType.Play.Client.STEER_VEHICLE,
PacketType.findCurrent(Protocol.PLAY, Sender.CLIENT, "SteerVehicle"));
}
@Test
public void testLoginStart() {
// This packet is critical for handleLoin
assertEquals(PacketLoginInStart.class, PacketType.Login.Client.START.getPacketClass());
}
@Test
public void testDeprecation() {
assertTrue(PacketType.Status.Server.OUT_SERVER_INFO.isDeprecated(), "Packet isn't properly deprecated");
assertTrue(PacketRegistry.getServerPacketTypes().contains(PacketType.Status.Server.OUT_SERVER_INFO),
"Deprecated packet isn't properly included");
assertFalse(PacketType.Play.Server.CHAT.isDeprecated(), "Packet isn't properly deprecated");
assertEquals(PacketType.Status.Server.OUT_SERVER_INFO, PacketType.Status.Server.SERVER_INFO,
"Deprecated packets aren't equal");
}
@Test
@SuppressWarnings("unchecked")
public void ensureTypesAreCorrect() throws Exception {
boolean fail = false;
EnumProtocol[] protocols = EnumProtocol.values();
for (EnumProtocol protocol : protocols) {
Field field = EnumProtocol.class.getDeclaredField("j");
field.setAccessible(true);
Map<EnumProtocolDirection, Object> map = (Map<EnumProtocolDirection, Object>) field.get(protocol);
for (Entry<EnumProtocolDirection, Object> entry : map.entrySet()) {
Field mapField = entry.getValue().getClass().getDeclaredField("b");
mapField.setAccessible(true);
Map<Class<?>, Integer> reverseMap = (Map<Class<?>, Integer>) mapField.get(entry.getValue());
Map<Integer, Class<?>> treeMap = new TreeMap<>();
for (Entry<Class<?>, Integer> entry1 : reverseMap.entrySet()) {
treeMap.put(entry1.getValue(), entry1.getKey());
}
for (Entry<Integer, Class<?>> entry1 : treeMap.entrySet()) {
try {
PacketType type = PacketType.fromClass(entry1.getValue());
if (type.getCurrentId() != entry1.getKey()) {
throw new IllegalStateException(
"Packet ID for " + type + " is incorrect. Expected " + entry1.getKey() + ", but got "
+ type.getCurrentId());
}
} catch (Throwable ex) {
ex.printStackTrace();
fail = true;
}
}
}
}
assertFalse(fail, "Packet type(s) were incorrect!");
}
@Test
public void testPacketCreation() {
boolean fail = false;
for (PacketType type : PacketType.values()) {
if (type.isSupported()) {
try {
new PacketContainer(type);
} catch (Exception ex) {
ex.printStackTrace();
fail = true;
}
}
}
assertFalse(fail, "Packet type(s) failed to instantiate");
}
}
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2016 dmulloy2
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.comphenix.protocol;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import net.minecraft.network.EnumProtocol;
import net.minecraft.network.protocol.EnumProtocolDirection;
import net.minecraft.network.protocol.login.PacketLoginInStart;
import org.apache.commons.lang.WordUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author dmulloy2
*/
public class PacketTypeTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
// I'm well aware this is jank, but it does in fact work correctly and give the desired result
PacketType.onDynamicCreate = className -> {
throw new RuntimeException("Dynamically generated packet " + className);
};
}
@AfterAll
public static void afterClass() {
PacketType.onDynamicCreate = __ -> {
};
}
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
MinecraftReflectionTestUtil.init();
Set<Class<?>> allTypes = new HashSet<>();
List<Class<?>> newTypes = new ArrayList<>();
EnumProtocol[] protocols = EnumProtocol.values();
for (EnumProtocol protocol : protocols) {
System.out.println(WordUtils.capitalize(protocol.name().toLowerCase()));
Field field = EnumProtocol.class.getDeclaredField("j");
field.setAccessible(true);
Map<EnumProtocolDirection, Object> map = (Map<EnumProtocolDirection, Object>) field.get(protocol);
for (Entry<EnumProtocolDirection, Object> entry : map.entrySet()) {
Field mapField = entry.getValue().getClass().getDeclaredField("b");
mapField.setAccessible(true);
Map<Class<?>, Integer> reverseMap = (Map<Class<?>, Integer>) mapField.get(entry.getValue());
Map<Integer, Class<?>> treeMap = new TreeMap<>();
for (Entry<Class<?>, Integer> entry1 : reverseMap.entrySet()) {
treeMap.put(entry1.getValue(), entry1.getKey());
}
System.out.println(" " + entry.getKey());
for (Entry<Integer, Class<?>> entry1 : treeMap.entrySet()) {
System.out.println(generateNewType(entry1.getKey(), entry1.getValue()));
allTypes.add(entry1.getValue());
try {
PacketType.fromClass(entry1.getValue());
} catch (Exception ex) {
newTypes.add(entry1.getValue());
}
}
}
}
System.out.println("New types: " + newTypes);
for (PacketType type : PacketType.values()) {
if (type.isDeprecated()) {
continue;
}
if (!allTypes.contains(type.getPacketClass())) {
System.out.println(type + " was removed");
}
}
}
private static String formatHex(int dec) {
if (dec < 0) {
return "0xFF";
}
String hex = Integer.toHexString(dec).toUpperCase();
return "0x" + (hex.length() < 2 ? "0" : "") + hex;
}
private static List<String> splitOnCaps(String string) {
List<String> list = new ArrayList<>();
StringBuilder builder = new StringBuilder();
char[] chars = string.toCharArray();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
if (i != 0 && Character.isUpperCase(c)) {
list.add(builder.toString());
builder = new StringBuilder();
}
builder.append(c);
}
list.add(builder.toString());
return list;
}
private static String generateNewType(int packetId, Class<?> clazz) {
StringBuilder builder = new StringBuilder();
builder.append("\t\t\t");
builder.append("public static final PacketType ");
String fullName = clazz.getName();
fullName = fullName.substring(fullName.lastIndexOf(".") + 1);
String className;
List<String> classNames = new ArrayList<>();
if (fullName.endsWith("Packet")) {
for (String name : fullName.split("\\$")) {
List<String> split = splitOnCaps(name);
StringBuilder nameBuilder = new StringBuilder();
for (int i = 1; i < split.size() - 1; i++) {
nameBuilder.append(split.get(i));
}
classNames.add(nameBuilder.toString());
}
} else {
for (String name : fullName.split("\\$")) {
List<String> split = splitOnCaps(name);
StringBuilder nameBuilder = new StringBuilder();
for (int i = 3; i < split.size(); i++) {
nameBuilder.append(split.get(i));
}
classNames.add(nameBuilder.toString());
}
}
className = classNames.get(classNames.size() - 1);
// Format it like SET_PROTOCOL
StringBuilder fieldName = new StringBuilder();
char[] chars = className.toCharArray();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
if (i != 0 && Character.isUpperCase(c)) {
fieldName.append("_");
}
fieldName.append(Character.toUpperCase(c));
}
builder.append(fieldName.toString().replace("N_B_T", "NBT"));
builder.append(" = ");
// Add spacing
if (builder.length() > 65) {
builder.append("\n");
} else {
while (builder.length() < 65) {
builder.append(" ");
}
}
builder.append("new ");
builder.append("PacketType(PROTOCOL, SENDER, ");
builder.append(formatHex(packetId));
builder.append(", ");
StringBuilder nameBuilder = new StringBuilder();
for (int i = 0; i < classNames.size(); i++) {
if (i != 0) {
nameBuilder.append("$");
}
nameBuilder.append(classNames.get(i));
}
String name = nameBuilder.toString();
String namesArg = listToString(getAllNames(clazz, name));
builder.append(namesArg);
builder.append(");");
return builder.toString();
}
private static List<String> getAllNames(Class<?> packetClass, String newName) {
List<String> names = new ArrayList<>();
names.add(newName);
try {
PacketType type = PacketType.fromClass(packetClass);
for (String alias : type.names) {
alias = alias.substring(alias.lastIndexOf('.') + 1);
if (!names.contains(alias)) {
names.add(alias);
}
}
} catch (Exception ignored) {
}
return names;
}
private static String listToString(List<String> list) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
if (i != 0) {
builder.append(", ");
}
builder.append("\"").append(list.get(i)).append("\"");
}
return builder.toString();
}
@BeforeAll
public static void initializeReflection() {
BukkitInitialization.initializeAll();
}
@Test
public void testFindCurrent() {
assertEquals(PacketType.Play.Client.STEER_VEHICLE,
PacketType.findCurrent(Protocol.PLAY, Sender.CLIENT, "SteerVehicle"));
}
@Test
public void testLoginStart() {
// This packet is critical for handleLoin
assertEquals(PacketLoginInStart.class, PacketType.Login.Client.START.getPacketClass());
}
@Test
public void testDeprecation() {
assertTrue(PacketType.Status.Server.OUT_SERVER_INFO.isDeprecated(), "Packet isn't properly deprecated");
assertTrue(PacketRegistry.getServerPacketTypes().contains(PacketType.Status.Server.OUT_SERVER_INFO),
"Deprecated packet isn't properly included");
assertFalse(PacketType.Play.Server.CHAT.isDeprecated(), "Packet isn't properly deprecated");
assertEquals(PacketType.Status.Server.OUT_SERVER_INFO, PacketType.Status.Server.SERVER_INFO,
"Deprecated packets aren't equal");
}
@Test
@SuppressWarnings("unchecked")
public void ensureTypesAreCorrect() throws Exception {
boolean fail = false;
EnumProtocol[] protocols = EnumProtocol.values();
for (EnumProtocol protocol : protocols) {
Field field = EnumProtocol.class.getDeclaredField("j");
field.setAccessible(true);
Map<EnumProtocolDirection, Object> map = (Map<EnumProtocolDirection, Object>) field.get(protocol);
for (Entry<EnumProtocolDirection, Object> entry : map.entrySet()) {
Field mapField = entry.getValue().getClass().getDeclaredField("b");
mapField.setAccessible(true);
Map<Class<?>, Integer> reverseMap = (Map<Class<?>, Integer>) mapField.get(entry.getValue());
Map<Integer, Class<?>> treeMap = new TreeMap<>();
for (Entry<Class<?>, Integer> entry1 : reverseMap.entrySet()) {
treeMap.put(entry1.getValue(), entry1.getKey());
}
for (Entry<Integer, Class<?>> entry1 : treeMap.entrySet()) {
try {
PacketType type = PacketType.fromClass(entry1.getValue());
if (type.getCurrentId() != entry1.getKey()) {
throw new IllegalStateException(
"Packet ID for " + type + " is incorrect. Expected " + entry1.getKey() + ", but got "
+ type.getCurrentId());
}
} catch (Throwable ex) {
ex.printStackTrace();
fail = true;
}
}
}
}
assertFalse(fail, "Packet type(s) were incorrect!");
}
@Test
public void testPacketCreation() {
boolean fail = false;
for (PacketType type : PacketType.values()) {
if (type.isSupported()) {
try {
new PacketContainer(type);
} catch (Exception ex) {
ex.printStackTrace();
fail = true;
}
}
}
assertFalse(fail, "Packet type(s) failed to instantiate");
}
}
File diff suppressed because it is too large. Load diff
@@ -1,59 +1,60 @@
package com.comphenix.protocol.injector;
import static com.comphenix.protocol.utility.TestUtils.setFinalField;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.lang.reflect.Field;
import net.minecraft.server.level.ChunkProviderServer;
import net.minecraft.server.level.PlayerChunkMap;
import net.minecraft.server.level.PlayerChunkMap.EntityTracker;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.entity.Entity;
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class EntityUtilitiesTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void testReflection() {
CraftWorld bukkit = mock(CraftWorld.class);
WorldServer world = mock(WorldServer.class);
when(bukkit.getHandle()).thenReturn(world);
ChunkProviderServer provider = mock(ChunkProviderServer.class);
when(world.k()).thenReturn(provider);
PlayerChunkMap chunkMap = mock(PlayerChunkMap.class);
Field chunkMapField = FuzzyReflection.fromClass(ChunkProviderServer.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(PlayerChunkMap.class).build());
setFinalField(provider, chunkMapField, chunkMap);
CraftEntity bukkitEntity = mock(CraftEntity.class);
Entity fakeEntity = mock(Entity.class);
when(fakeEntity.getBukkitEntity()).thenReturn(bukkitEntity);
EntityTracker tracker = mock(EntityTracker.class);
Field trackerField = FuzzyReflection.fromClass(EntityTracker.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(Entity.class).build());
setFinalField(tracker, trackerField, fakeEntity);
Int2ObjectMap<EntityTracker> trackerMap = new Int2ObjectOpenHashMap<>();
trackerMap.put(1, tracker);
Field trackedEntitiesField = FuzzyReflection.fromClass(PlayerChunkMap.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(Int2ObjectMap.class).build());
setFinalField(chunkMap, trackedEntitiesField, trackerMap);
}
}
package com.comphenix.protocol.injector;
import java.lang.reflect.Field;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.server.level.ChunkProviderServer;
import net.minecraft.server.level.PlayerChunkMap;
import net.minecraft.server.level.PlayerChunkMap.EntityTracker;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.entity.Entity;
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.setFinalField;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class EntityUtilitiesTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void testReflection() {
CraftWorld bukkit = mock(CraftWorld.class);
WorldServer world = mock(WorldServer.class);
when(bukkit.getHandle()).thenReturn(world);
ChunkProviderServer provider = mock(ChunkProviderServer.class);
when(world.k()).thenReturn(provider);
PlayerChunkMap chunkMap = mock(PlayerChunkMap.class);
Field chunkMapField = FuzzyReflection.fromClass(ChunkProviderServer.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(PlayerChunkMap.class).build());
setFinalField(provider, chunkMapField, chunkMap);
CraftEntity bukkitEntity = mock(CraftEntity.class);
Entity fakeEntity = mock(Entity.class);
when(fakeEntity.getBukkitEntity()).thenReturn(bukkitEntity);
EntityTracker tracker = mock(EntityTracker.class);
Field trackerField = FuzzyReflection.fromClass(EntityTracker.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(Entity.class).build());
setFinalField(tracker, trackerField, fakeEntity);
Int2ObjectMap<EntityTracker> trackerMap = new Int2ObjectOpenHashMap<>();
trackerMap.put(1, tracker);
Field trackedEntitiesField = FuzzyReflection.fromClass(PlayerChunkMap.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(Int2ObjectMap.class).build());
setFinalField(chunkMap, trackedEntitiesField, trackerMap);
}
}
@@ -1,69 +1,69 @@
/**
* (c) 2016 dmulloy2
*/
package com.comphenix.protocol.injector;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.netty.WirePacket;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author dmulloy2
*/
public class WirePacketTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
// @Test
public void testPackets() {
List<String> failures = new ArrayList<>();
for (PacketType type : PacketType.values()) {
if (type.isDeprecated()) {
continue;
}
try {
PacketContainer packet = new PacketContainer(type);
WirePacket wire = WirePacket.fromPacket(packet);
WirePacket handle = WirePacket.fromPacket(packet.getHandle());
assertEquals(wire, handle);
} catch (Exception ex) {
failures.add(type + " :: " + ex.getMessage());
System.out.println(type);
ex.printStackTrace();
}
}
assertEquals(failures, new ArrayList<>());
}
@Test
public void testSerialization() {
int id = 42;
byte[] array = {1, 3, 7, 21, 88, 67, 8};
WirePacket packet = new WirePacket(id, array);
ByteBuf buf = packet.serialize();
int backId = WirePacket.readVarInt(buf);
byte[] backArray = new byte[buf.readableBytes()];
buf.readBytes(backArray);
assertEquals(id, backId);
assertArrayEquals(array, backArray);
}
/**
* (c) 2016 dmulloy2
*/
package com.comphenix.protocol.injector;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.netty.WirePacket;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author dmulloy2
*/
public class WirePacketTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
// @Test
public void testPackets() {
List<String> failures = new ArrayList<>();
for (PacketType type : PacketType.values()) {
if (type.isDeprecated()) {
continue;
}
try {
PacketContainer packet = new PacketContainer(type);
WirePacket wire = WirePacket.fromPacket(packet);
WirePacket handle = WirePacket.fromPacket(packet.getHandle());
assertEquals(wire, handle);
} catch (Exception ex) {
failures.add(type + " :: " + ex.getMessage());
System.out.println(type);
ex.printStackTrace();
}
}
assertEquals(failures, new ArrayList<>());
}
@Test
public void testSerialization() {
int id = 42;
byte[] array = {1, 3, 7, 21, 88, 67, 8};
WirePacket packet = new WirePacket(id, array);
ByteBuf buf = packet.serialize();
int backId = WirePacket.readVarInt(buf);
byte[] backArray = new byte[buf.readableBytes()];
buf.readBytes(backArray);
assertEquals(id, backId);
assertArrayEquals(array, backArray);
}
}
@@ -1,45 +1,45 @@
package com.comphenix.protocol.reflect.cloning;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import java.util.Arrays;
import java.util.List;
import net.minecraft.core.NonNullList;
import net.minecraft.world.item.ItemStack;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class AggregateClonerTest {
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@Test
public void testArrays() {
List<Integer> input = Arrays.asList(1, 2, 3);
assertEquals(input, AggregateCloner.DEFAULT.clone(input));
}
// @Test
// Usages of NonNullList were removed in 1.17.1
public void testNonNullList() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WINDOW_ITEMS);
NonNullList<ItemStack> list = NonNullList.a(16, ItemStack.b);
packet.getModifier().write(1, list);
PacketContainer cloned = packet.deepClone();
@SuppressWarnings("unchecked")
NonNullList<ItemStack> list1 = (NonNullList<ItemStack>) cloned.getModifier().read(1);
assertEquals(list.size(), list1.size());
Assertions.assertArrayEquals(list.toArray(), list1.toArray());
}
}
package com.comphenix.protocol.reflect.cloning;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import java.util.Arrays;
import java.util.List;
import net.minecraft.core.NonNullList;
import net.minecraft.world.item.ItemStack;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class AggregateClonerTest {
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@Test
public void testArrays() {
List<Integer> input = Arrays.asList(1, 2, 3);
assertEquals(input, AggregateCloner.DEFAULT.clone(input));
}
// @Test
// Usages of NonNullList were removed in 1.17.1
public void testNonNullList() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WINDOW_ITEMS);
NonNullList<ItemStack> list = NonNullList.a(16, ItemStack.b);
packet.getModifier().write(1, list);
PacketContainer cloned = packet.deepClone();
@SuppressWarnings("unchecked")
NonNullList<ItemStack> list1 = (NonNullList<ItemStack>) cloned.getModifier().read(1);
assertEquals(list.size(), list1.size());
Assertions.assertArrayEquals(list.toArray(), list1.toArray());
}
}
@@ -1,159 +1,160 @@
package com.comphenix.protocol.utility;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import com.comphenix.protocol.BukkitInitialization;
import com.mojang.authlib.GameProfile;
import net.minecraft.nbt.NBTCompressedStreamTools;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes;
import net.minecraft.network.protocol.status.ServerPing;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.server.network.PlayerConnection;
import net.minecraft.util.MinecraftEncryption;
import net.minecraft.world.level.ChunkCoordIntPair;
import net.minecraft.world.level.block.state.IBlockData;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class MinecraftReflectionTest {
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@AfterAll
public static void undoMocking() {
// NOP
MinecraftReflection.minecraftPackage = null;
}
@Test
public void testBukkitMethod() {
FakeEntity entity = mock(FakeEntity.class);
FakeBlock block = mock(FakeBlock.class);
MinecraftReflection.getBukkitEntity(entity);
MinecraftReflection.getBukkitEntity(block);
verify(entity, times(1)).getBukkitEntity();
verify(block, times(1)).getBukkitEntity();
}
@Test
public void testIllegalClass() {
assertThrows(IllegalArgumentException.class, () -> MinecraftReflection.getBukkitEntity("Hello"));
}
@Test
public void testNullable() {
assertNull(MinecraftReflection.getNullableNMS("ProtocolLib"));
}
@Test
public void testAttributeSnapshot() {
assertEquals(PacketPlayOutUpdateAttributes.AttributeSnapshot.class,
MinecraftReflection.getAttributeSnapshotClass());
}
@Test
public void testChatComponent() {
assertEquals(IChatBaseComponent.class, MinecraftReflection.getIChatBaseComponentClass());
}
@Test
public void testChatSerializer() {
assertEquals(IChatBaseComponent.ChatSerializer.class, MinecraftReflection.getChatSerializerClass());
}
@Test
public void testChunkCoordIntPair() {
assertEquals(ChunkCoordIntPair.class, MinecraftReflection.getChunkCoordIntPair());
}
@Test
public void testIBlockData() {
assertEquals(IBlockData.class, MinecraftReflection.getIBlockDataClass());
}
@Test
public void testPlayerConnection() {
assertEquals(PlayerConnection.class, MinecraftReflection.getPlayerConnectionClass());
}
@Test
public void testServerPing() {
assertEquals(ServerPing.class, MinecraftReflection.getServerPingClass());
}
@Test
public void testServerPingPlayerSample() {
assertEquals(ServerPing.ServerPingPlayerSample.class, MinecraftReflection.getServerPingPlayerSampleClass());
}
@Test
public void testServerPingServerData() {
assertEquals(ServerPing.ServerData.class, MinecraftReflection.getServerPingServerDataClass());
}
@Test
public void testNbtStreamTools() {
assertEquals(NBTCompressedStreamTools.class, MinecraftReflection.getNbtCompressedStreamToolsClass());
}
@Test
public void testDataWatcherItem() {
assertEquals(DataWatcher.Item.class, MinecraftReflection.getDataWatcherItemClass());
}
@Test
public void testLoginSignature() {
assertEquals(MinecraftEncryption.b.class, MinecraftReflection.getSaltedSignatureClass());
}
@Test
public void testItemStacks() {
ItemStack stack = new ItemStack(Material.GOLDEN_SWORD);
Object nmsStack = MinecraftReflection.getMinecraftItemStack(stack);
assertItemsEqual(stack, MinecraftReflection.getBukkitItemStack(nmsStack));
// The NMS handle for CraftItemStack is null with Material.AIR, make sure it is handled correctly
assertNotNull(MinecraftReflection.getMinecraftItemStack(CraftItemStack.asCraftCopy(new ItemStack(Material.AIR))));
}
@Test
public void testGameProfile() {
assertEquals(GameProfile.class, MinecraftReflection.getGameProfileClass());
}
@Test
public void testEnumEntityUseAction() {
// this class is package-private in PacketPlayInUseEntity, so we can only check if no exception is thrown during retrieval
MinecraftReflection.getEnumEntityUseActionClass();
}
// Mocking objects
private interface FakeEntity {
Entity getBukkitEntity();
}
private interface FakeBlock {
Block getBukkitEntity();
}
}
package com.comphenix.protocol.utility;
import com.comphenix.protocol.BukkitInitialization;
import com.mojang.authlib.GameProfile;
import net.minecraft.nbt.NBTCompressedStreamTools;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes;
import net.minecraft.network.protocol.status.ServerPing;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.server.network.PlayerConnection;
import net.minecraft.util.MinecraftEncryption;
import net.minecraft.world.level.ChunkCoordIntPair;
import net.minecraft.world.level.block.state.IBlockData;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
public class MinecraftReflectionTest {
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@AfterAll
public static void undoMocking() {
// NOP
MinecraftReflection.minecraftPackage = null;
}
@Test
public void testBukkitMethod() {
FakeEntity entity = mock(FakeEntity.class);
FakeBlock block = mock(FakeBlock.class);
MinecraftReflection.getBukkitEntity(entity);
MinecraftReflection.getBukkitEntity(block);
verify(entity, times(1)).getBukkitEntity();
verify(block, times(1)).getBukkitEntity();
}
@Test
public void testIllegalClass() {
assertThrows(IllegalArgumentException.class, () -> MinecraftReflection.getBukkitEntity("Hello"));
}
@Test
public void testNullable() {
assertNull(MinecraftReflection.getNullableNMS("ProtocolLib"));
}
@Test
public void testAttributeSnapshot() {
assertEquals(PacketPlayOutUpdateAttributes.AttributeSnapshot.class,
MinecraftReflection.getAttributeSnapshotClass());
}
@Test
public void testChatComponent() {
assertEquals(IChatBaseComponent.class, MinecraftReflection.getIChatBaseComponentClass());
}
@Test
public void testChatSerializer() {
assertEquals(IChatBaseComponent.ChatSerializer.class, MinecraftReflection.getChatSerializerClass());
}
@Test
public void testChunkCoordIntPair() {
assertEquals(ChunkCoordIntPair.class, MinecraftReflection.getChunkCoordIntPair());
}
@Test
public void testIBlockData() {
assertEquals(IBlockData.class, MinecraftReflection.getIBlockDataClass());
}
@Test
public void testPlayerConnection() {
assertEquals(PlayerConnection.class, MinecraftReflection.getPlayerConnectionClass());
}
@Test
public void testServerPing() {
assertEquals(ServerPing.class, MinecraftReflection.getServerPingClass());
}
@Test
public void testServerPingPlayerSample() {
assertEquals(ServerPing.ServerPingPlayerSample.class, MinecraftReflection.getServerPingPlayerSampleClass());
}
@Test
public void testServerPingServerData() {
assertEquals(ServerPing.ServerData.class, MinecraftReflection.getServerPingServerDataClass());
}
@Test
public void testNbtStreamTools() {
assertEquals(NBTCompressedStreamTools.class, MinecraftReflection.getNbtCompressedStreamToolsClass());
}
@Test
public void testDataWatcherItem() {
assertEquals(DataWatcher.Item.class, MinecraftReflection.getDataWatcherItemClass());
}
@Test
public void testLoginSignature() {
assertEquals(MinecraftEncryption.b.class, MinecraftReflection.getSaltedSignatureClass());
}
@Test
public void testItemStacks() {
ItemStack stack = new ItemStack(Material.GOLDEN_SWORD);
Object nmsStack = MinecraftReflection.getMinecraftItemStack(stack);
assertItemsEqual(stack, MinecraftReflection.getBukkitItemStack(nmsStack));
// The NMS handle for CraftItemStack is null with Material.AIR, make sure it is handled correctly
assertNotNull(
MinecraftReflection.getMinecraftItemStack(CraftItemStack.asCraftCopy(new ItemStack(Material.AIR))));
}
@Test
public void testGameProfile() {
assertEquals(GameProfile.class, MinecraftReflection.getGameProfileClass());
}
@Test
public void testEnumEntityUseAction() {
// this class is package-private in PacketPlayInUseEntity, so we can only check if no exception is thrown during retrieval
MinecraftReflection.getEnumEntityUseActionClass();
}
// Mocking objects
private interface FakeEntity {
Entity getBukkitEntity();
}
private interface FakeBlock {
Block getBukkitEntity();
}
}
@@ -2,7 +2,8 @@ package com.comphenix.protocol.utility;
public class MinecraftReflectionTestUtil {
public static final String PACKAGE_VERSION = "v1_19_R1";
public static final String RELEASE_TARGET = "1.19.3";
public static final String PACKAGE_VERSION = "v1_19_R2";
public static final String NMS = "net.minecraft";
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
@@ -1,52 +1,52 @@
package com.comphenix.protocol.utility;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.reflect.accessors.Accessors;
import java.lang.reflect.Field;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
public class TestUtils {
public static void assertItemCollectionsEqual(List<ItemStack> first, List<ItemStack> second) {
assertEquals(first.size(), second.size());
for (int i = 0; i < first.size(); i++) {
assertItemsEqual(first.get(i), second.get(i));
}
}
public static void assertItemsEqual(ItemStack first, ItemStack second) {
if (first == null) {
assertNull(second);
} else {
assertNotNull(first);
// The legacy check in ItemStack#isSimilar causes a null pointer
assertEquals(first.getType(), second.getType());
assertEquals(first.getDurability(), second.getDurability());
assertEquals(first.hasItemMeta(), second.hasItemMeta());
if (first.hasItemMeta()) {
assertTrue(Bukkit.getItemFactory().equals(first.getItemMeta(), second.getItemMeta()));
}
}
}
public static boolean equivalentItem(ItemStack first, ItemStack second) {
if (first == null) {
return second == null;
} else if (second == null) {
return false;
} else {
return first.getType().equals(second.getType());
}
}
public static void setFinalField(Object obj, Field field, Object newValue) {
Accessors.getFieldAccessor(field).set(obj, newValue);
}
}
package com.comphenix.protocol.utility;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.reflect.accessors.Accessors;
import java.lang.reflect.Field;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
public class TestUtils {
public static void assertItemCollectionsEqual(List<ItemStack> first, List<ItemStack> second) {
assertEquals(first.size(), second.size());
for (int i = 0; i < first.size(); i++) {
assertItemsEqual(first.get(i), second.get(i));
}
}
public static void assertItemsEqual(ItemStack first, ItemStack second) {
if (first == null) {
assertNull(second);
} else {
assertNotNull(first);
// The legacy check in ItemStack#isSimilar causes a null pointer
assertEquals(first.getType(), second.getType());
assertEquals(first.getDurability(), second.getDurability());
assertEquals(first.hasItemMeta(), second.hasItemMeta());
if (first.hasItemMeta()) {
assertTrue(Bukkit.getItemFactory().equals(first.getItemMeta(), second.getItemMeta()));
}
}
}
public static boolean equivalentItem(ItemStack first, ItemStack second) {
if (first == null) {
return second == null;
} else if (second == null) {
return false;
} else {
return first.getType().equals(second.getType());
}
}
public static void setFinalField(Object obj, Field field, Object newValue) {
Accessors.getFieldAccessor(field).set(obj, newValue);
}
}
@@ -30,7 +30,7 @@ public class AutoWrapperTest {
WrappedAdvancementDisplay display = new WrappedAdvancementDisplay();
display.title = WrappedChatComponent.fromText("Test123");
display.description = WrappedChatComponent.fromText("Test567");
display.item = new ItemStack(Material.DARK_OAK_SAPLING);
display.item = new ItemStack(Material.GOLD_INGOT);
display.background = new MinecraftKey("test");
display.frameType = WrappedFrameType.CHALLENGE;
display.announceChat = false;
@@ -48,7 +48,7 @@ public class AutoWrapperTest {
validateRawText(nms.a(), "Test123");
validateRawText(nms.b(), "Test567");
assertSame(AdvancementFrameType.b, nms.e());
assertSame(Items.L, nms.c().c());
assertSame(Items.nv, nms.c().c());
assertEquals(5f, nms.f(), 0f);
assertEquals(67f, nms.g(), 0f);
}
@@ -56,7 +56,7 @@ public class AutoWrapperTest {
@Test
public void testFromNms() {
AdvancementDisplay display = new AdvancementDisplay(
new net.minecraft.world.item.ItemStack(Items.qA),
new net.minecraft.world.item.ItemStack(Items.rc),
IChatBaseComponent.b("Test123"),
IChatBaseComponent.b("Test567"),
new net.minecraft.resources.MinecraftKey("minecraft", "test"),
@@ -1,58 +1,58 @@
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.wrappers.Either.Left;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class BukkitConvertersTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void testItemStacks() {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD, 16);
item.addEnchantment(Enchantment.DAMAGE_ALL, 4);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Diamond Sword");
item.setItemMeta(meta);
EquivalentConverter<ItemStack> converter = BukkitConverters.getItemStackConverter();
Object nmsStack = converter.getGeneric(item);
ItemStack back = converter.getSpecific(nmsStack);
assertEquals(item.getType(), back.getType());
assertEquals(item.getDurability(), back.getDurability());
assertEquals(item.hasItemMeta(), back.hasItemMeta());
assertTrue(Bukkit.getItemFactory().equals(item.getItemMeta(), back.getItemMeta()));
}
@Test
public void testEither() {
Either<String, String> test = new Left<>("bla");
EquivalentConverter<Either<String, String>> converter = BukkitConverters.getEitherConverter(
Converters.passthrough(String.class), Converters.passthrough(String.class)
);
com.mojang.datafixers.util.Either<String, String> nmsEither = (com.mojang.datafixers.util.Either<String, String>) converter.getGeneric(test);
Either<String, String> wrapped = converter.getSpecific(nmsEither);
assertEquals(wrapped.left(), nmsEither.left());
assertEquals(wrapped.right(), nmsEither.right());
}
}
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.wrappers.Either.Left;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class BukkitConvertersTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void testItemStacks() {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD, 16);
item.addEnchantment(Enchantment.DAMAGE_ALL, 4);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Diamond Sword");
item.setItemMeta(meta);
EquivalentConverter<ItemStack> converter = BukkitConverters.getItemStackConverter();
Object nmsStack = converter.getGeneric(item);
ItemStack back = converter.getSpecific(nmsStack);
assertEquals(item.getType(), back.getType());
assertEquals(item.getDurability(), back.getDurability());
assertEquals(item.hasItemMeta(), back.hasItemMeta());
assertTrue(Bukkit.getItemFactory().equals(item.getItemMeta(), back.getItemMeta()));
}
@Test
public void testEither() {
Either<String, String> test = new Left<>("bla");
EquivalentConverter<Either<String, String>> converter = BukkitConverters.getEitherConverter(
Converters.passthrough(String.class), Converters.passthrough(String.class)
);
com.mojang.datafixers.util.Either<String, String> nmsEither = (com.mojang.datafixers.util.Either<String, String>) converter.getGeneric(test);
Either<String, String> wrapped = converter.getSpecific(nmsEither);
assertEquals(wrapped.left(), nmsEither.left());
assertEquals(wrapped.right(), nmsEither.right());
}
}
@@ -1,31 +1,31 @@
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class ChunkCoordIntPairTest {
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@Test
public void test() {
net.minecraft.world.level.ChunkCoordIntPair pair = new net.minecraft.world.level.ChunkCoordIntPair(1, 2);
ChunkCoordIntPair specific = ChunkCoordIntPair.getConverter().getSpecific(pair);
assertEquals(1, specific.getChunkX());
assertEquals(2, specific.getChunkZ());
net.minecraft.world.level.ChunkCoordIntPair roundtrip =
(net.minecraft.world.level.ChunkCoordIntPair) ChunkCoordIntPair.getConverter().
getGeneric(specific);
assertEquals(1, roundtrip.e);
assertEquals(2, roundtrip.f);
}
}
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class ChunkCoordIntPairTest {
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@Test
public void test() {
net.minecraft.world.level.ChunkCoordIntPair pair = new net.minecraft.world.level.ChunkCoordIntPair(1, 2);
ChunkCoordIntPair specific = ChunkCoordIntPair.getConverter().getSpecific(pair);
assertEquals(1, specific.getChunkX());
assertEquals(2, specific.getChunkZ());
net.minecraft.world.level.ChunkCoordIntPair roundtrip =
(net.minecraft.world.level.ChunkCoordIntPair) ChunkCoordIntPair.getConverter().
getGeneric(specific);
assertEquals(1, roundtrip.e);
assertEquals(2, roundtrip.f);
}
}
@@ -1,57 +1,58 @@
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.google.common.collect.Sets;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class EnumWrappersTest {
private static final Set<String> KNOWN_INVALID = Sets.newHashSet(
"Particle", "WorldBorderAction", "CombatEventType", "TitleAction", "ChatType"
);
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
EnumWrappers.getPlayerInfoActionClass(); // just to initialize the classes and converters
}
@Test
@SuppressWarnings("unchecked")
public void validateAllEnumFieldsAreWrapped() {
Map<Class<?>, EquivalentConverter<?>> nativeEnums = EnumWrappers.getFromNativeMap();
for (Entry<Class<?>, EquivalentConverter<?>> entry : nativeEnums.entrySet()) {
for (Object nativeConstant : entry.getKey().getEnumConstants()) {
try {
// yay, generics
EquivalentConverter<Object> converter = (EquivalentConverter<Object>) entry.getValue();
// try to convert the native constant to a wrapper and back
Object wrappedValue = converter.getSpecific(nativeConstant);
assertNotNull(wrappedValue);
Object unwrappedValue = converter.getGeneric(wrappedValue);
assertNotNull(unwrappedValue);
assertEquals(nativeConstant, unwrappedValue);
} catch (Exception exception) {
fail(exception);
}
}
}
}
@Test
public void testValidity() {
assertEquals(EnumWrappers.INVALID, KNOWN_INVALID);
}
}
package com.comphenix.protocol.wrappers;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.google.common.collect.Sets;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
public class EnumWrappersTest {
private static final Set<String> KNOWN_INVALID = Sets.newHashSet(
"Particle", "WorldBorderAction", "CombatEventType", "TitleAction", "ChatType", "TitleAction"
);
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
EnumWrappers.getPlayerInfoActionClass(); // just to initialize the classes and converters
}
@Test
@SuppressWarnings("unchecked")
public void validateAllEnumFieldsAreWrapped() {
Map<Class<?>, EquivalentConverter<?>> nativeEnums = EnumWrappers.getFromNativeMap();
for (Entry<Class<?>, EquivalentConverter<?>> entry : nativeEnums.entrySet()) {
for (Object nativeConstant : entry.getKey().getEnumConstants()) {
try {
// yay, generics
EquivalentConverter<Object> converter = (EquivalentConverter<Object>) entry.getValue();
// try to convert the native constant to a wrapper and back
Object wrappedValue = converter.getSpecific(nativeConstant);
assertNotNull(wrappedValue);
Object unwrappedValue = converter.getGeneric(wrappedValue);
assertNotNull(unwrappedValue);
assertEquals(nativeConstant, unwrappedValue);
} catch (Exception exception) {
fail(exception);
}
}
}
}
@Test
public void testValidity() {
assertEquals(EnumWrappers.INVALID, KNOWN_INVALID);
}
}
@@ -1,104 +1,104 @@
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.WrappedAttributeModifier.Operation;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.core.IRegistry;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.world.entity.ai.attributes.AttributeBase;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class WrappedAttributeTest {
private WrappedAttributeModifier doubleModifier;
private WrappedAttributeModifier constantModifier;
private WrappedAttribute attribute;
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@BeforeEach
public void setUp() {
// Create a couple of modifiers
this.doubleModifier =
WrappedAttributeModifier.newBuilder().
name("Double Damage").
amount(1).
operation(Operation.ADD_PERCENTAGE).
build();
this.constantModifier =
WrappedAttributeModifier.newBuilder().
name("Damage Bonus").
amount(5).
operation(Operation.ADD_NUMBER).
build();
// Create attribute
this.attribute = WrappedAttribute.newBuilder().
attributeKey("generic.attackDamage").
baseValue(2).
packet(new PacketContainer(PacketType.Play.Server.UPDATE_ATTRIBUTES)).
modifiers(Lists.newArrayList(this.constantModifier, this.doubleModifier)).
build();
}
@Test
public void testEquality() {
// Check wrapped equality
assertEquals(this.doubleModifier, this.doubleModifier);
assertNotSame(this.constantModifier, this.doubleModifier);
assertEquals(this.doubleModifier.getHandle(), this.getModifierCopy(this.doubleModifier));
assertEquals(this.constantModifier.getHandle(), this.getModifierCopy(this.constantModifier));
}
@Test
public void testAttribute() {
assertEquals(this.attribute, WrappedAttribute.fromHandle(this.getAttributeCopy(this.attribute)));
assertTrue(this.attribute.hasModifier(this.doubleModifier.getUUID()));
assertTrue(this.attribute.hasModifier(this.constantModifier.getUUID()));
}
@Test
public void testFromTemplate() {
assertEquals(this.attribute, WrappedAttribute.newBuilder(this.attribute).build());
}
/**
* Retrieve the equivalent NMS attribute.
*
* @param attribute - the wrapped attribute.
* @return The equivalent NMS attribute.
*/
private AttributeSnapshot getAttributeCopy(WrappedAttribute attribute) {
List<AttributeModifier> modifiers = new ArrayList<>();
for (WrappedAttributeModifier wrapper : attribute.getModifiers()) {
modifiers.add((AttributeModifier) wrapper.getHandle());
}
AttributeBase base = IRegistry.ak.a(MinecraftKey.a(attribute.getAttributeKey()));
return new AttributeSnapshot(base, attribute.getBaseValue(), modifiers);
}
private AttributeModifier getModifierCopy(WrappedAttributeModifier modifier) {
AttributeModifier.Operation operation = AttributeModifier.Operation.values()[modifier.getOperation().getId()];
return new AttributeModifier(modifier.getUUID(), modifier.getName(), modifier.getAmount(), operation);
}
}
package com.comphenix.protocol.wrappers;
import java.util.ArrayList;
import java.util.List;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.WrappedAttributeModifier.Operation;
import com.google.common.collect.Lists;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.world.entity.ai.attributes.AttributeBase;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class WrappedAttributeTest {
private WrappedAttributeModifier doubleModifier;
private WrappedAttributeModifier constantModifier;
private WrappedAttribute attribute;
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@BeforeEach
public void setUp() {
// Create a couple of modifiers
this.doubleModifier =
WrappedAttributeModifier.newBuilder().
name("Double Damage").
amount(1).
operation(Operation.ADD_PERCENTAGE).
build();
this.constantModifier =
WrappedAttributeModifier.newBuilder().
name("Damage Bonus").
amount(5).
operation(Operation.ADD_NUMBER).
build();
// Create attribute
this.attribute = WrappedAttribute.newBuilder().
attributeKey("generic.attackDamage").
baseValue(2).
packet(new PacketContainer(PacketType.Play.Server.UPDATE_ATTRIBUTES)).
modifiers(Lists.newArrayList(this.constantModifier, this.doubleModifier)).
build();
}
@Test
public void testEquality() {
// Check wrapped equality
assertEquals(this.doubleModifier, this.doubleModifier);
assertNotSame(this.constantModifier, this.doubleModifier);
assertEquals(this.doubleModifier.getHandle(), this.getModifierCopy(this.doubleModifier));
assertEquals(this.constantModifier.getHandle(), this.getModifierCopy(this.constantModifier));
}
@Test
public void testAttribute() {
assertEquals(this.attribute, WrappedAttribute.fromHandle(this.getAttributeCopy(this.attribute)));
assertTrue(this.attribute.hasModifier(this.doubleModifier.getUUID()));
assertTrue(this.attribute.hasModifier(this.constantModifier.getUUID()));
}
@Test
public void testFromTemplate() {
assertEquals(this.attribute, WrappedAttribute.newBuilder(this.attribute).build());
}
/**
* Retrieve the equivalent NMS attribute.
*
* @param attribute - the wrapped attribute.
* @return The equivalent NMS attribute.
*/
private AttributeSnapshot getAttributeCopy(WrappedAttribute attribute) {
List<AttributeModifier> modifiers = new ArrayList<>();
for (WrappedAttributeModifier wrapper : attribute.getModifiers()) {
modifiers.add((AttributeModifier) wrapper.getHandle());
}
AttributeBase base = BuiltInRegistries.u.a(MinecraftKey.a(attribute.getAttributeKey()));
return new AttributeSnapshot(base, attribute.getBaseValue(), modifiers);
}
private AttributeModifier getModifierCopy(WrappedAttributeModifier modifier) {
AttributeModifier.Operation operation = AttributeModifier.Operation.values()[modifier.getOperation().getId()];
return new AttributeModifier(modifier.getUUID(), modifier.getName(), modifier.getAmount(), operation);
}
}
@@ -1,70 +1,70 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2015 dmulloy2
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import net.minecraft.world.level.block.state.IBlockData;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.GlassPane;
import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_19_R1.block.impl.CraftStainedGlassPane;
import org.bukkit.craftbukkit.v1_19_R1.util.CraftMagicNumbers;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author dmulloy2
*/
public class WrappedBlockDataTest {
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@Test
public void testMaterialCreation() {
Material type = Material.BLUE_WOOL;
WrappedBlockData wrapper = WrappedBlockData.createData(type);
assertEquals(wrapper.getType(), type);
//assertEquals(wrapper.getData(), data);
Object generic = BukkitConverters.getWrappedBlockDataConverter().getGeneric(wrapper);
WrappedBlockData back = BukkitConverters.getWrappedBlockDataConverter().getSpecific(generic);
assertEquals(wrapper.getType(), back.getType());
assertEquals(wrapper.getData(), back.getData());
}
@Test
public void testDataCreation() {
IBlockData nmsData = CraftMagicNumbers.getBlock(Material.CYAN_STAINED_GLASS_PANE).m();
GlassPane data = (GlassPane) CraftBlockData.fromData(nmsData);
data.setFace(BlockFace.EAST, true);
WrappedBlockData wrapper = WrappedBlockData.createData(data);
assertEquals(wrapper.getType(), Material.CYAN_STAINED_GLASS_PANE);
GlassPane back = new CraftStainedGlassPane((IBlockData) wrapper.getHandle());
assertEquals(back.hasFace(BlockFace.EAST), data.hasFace(BlockFace.EAST));
assertEquals(back.hasFace(BlockFace.SOUTH), data.hasFace(BlockFace.SOUTH));
}
}
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2015 dmulloy2
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.BukkitInitialization;
import net.minecraft.world.level.block.state.IBlockData;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.GlassPane;
import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_19_R2.block.impl.CraftStainedGlassPane;
import org.bukkit.craftbukkit.v1_19_R2.util.CraftMagicNumbers;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author dmulloy2
*/
public class WrappedBlockDataTest {
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@Test
public void testMaterialCreation() {
Material type = Material.BLUE_WOOL;
WrappedBlockData wrapper = WrappedBlockData.createData(type);
assertEquals(wrapper.getType(), type);
//assertEquals(wrapper.getData(), data);
Object generic = BukkitConverters.getWrappedBlockDataConverter().getGeneric(wrapper);
WrappedBlockData back = BukkitConverters.getWrappedBlockDataConverter().getSpecific(generic);
assertEquals(wrapper.getType(), back.getType());
assertEquals(wrapper.getData(), back.getData());
}
@Test
public void testDataCreation() {
IBlockData nmsData = CraftMagicNumbers.getBlock(Material.CYAN_STAINED_GLASS_PANE).n();
GlassPane data = (GlassPane) CraftBlockData.fromData(nmsData);
data.setFace(BlockFace.EAST, true);
WrappedBlockData wrapper = WrappedBlockData.createData(data);
assertEquals(wrapper.getType(), Material.CYAN_STAINED_GLASS_PANE);
GlassPane back = new CraftStainedGlassPane((IBlockData) wrapper.getHandle());
assertEquals(back.hasFace(BlockFace.EAST), data.hasFace(BlockFace.EAST));
assertEquals(back.hasFace(BlockFace.SOUTH), data.hasFace(BlockFace.SOUTH));
}
}
@@ -1,115 +1,116 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2016 dmulloy2
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import java.util.UUID;
import net.minecraft.world.entity.projectile.EntityEgg;
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEgg;
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author dmulloy2
*/
public class WrappedDataWatcherTest {
@BeforeAll
public static void prepare() {
BukkitInitialization.initializeAll();
}
@Test
public void testBytes() {
// Create a fake lightning strike and get its watcher
EntityEgg nmsEgg = new EntityEgg(null, 0, 0, 0);
CraftEntity craftEgg = new CraftEgg(null, nmsEgg);
WrappedDataWatcher wrapper = WrappedDataWatcher.getEntityWatcher(craftEgg);
WrappedWatchableObject watchable = wrapper.getWatchableObject(0);
WrappedDataWatcherObject object = watchable.getWatcherObject();
// Make sure the serializers work
assertEquals(object.getSerializer(), Registry.get(Byte.class));
// Make sure we can set existing objects
wrapper.setObject(0, (byte) 21);
assertEquals(21, (byte) wrapper.getByte(0));
}
@Test
public void testStrings() {
WrappedDataWatcher wrapper = new WrappedDataWatcher();
// Make sure we can create watcher objects
Serializer serializer = Registry.get(String.class);
WrappedDataWatcherObject object = new WrappedDataWatcherObject(3, serializer);
wrapper.setObject(object, "Test");
assertEquals(wrapper.getString(3), "Test");
}
@Test
public void testFloats() {
WrappedDataWatcher wrapper = new WrappedDataWatcher();
// Make sure we can add new entries
Serializer serializer = Registry.get(Float.class);
WrappedDataWatcherObject object = new WrappedDataWatcherObject(10, serializer);
wrapper.setObject(object, 21.0F);
assertTrue(wrapper.hasIndex(10));
}
@Test
public void testSerializers() {
Serializer blockPos = Registry.get(net.minecraft.core.BlockPosition.class, false);
Serializer optionalBlockPos = Registry.get(net.minecraft.core.BlockPosition.class, true);
assertNotSame(blockPos, optionalBlockPos);
// assertNull(Registry.get(ItemStack.class, false));
assertNotNull(Registry.get(UUID.class, true));
}
@Test
public void testHasIndex() {
WrappedDataWatcher watcher = new WrappedDataWatcher();
Serializer serializer = Registry.get(Integer.class);
assertFalse(watcher.hasIndex(0));
watcher.setObject(0, serializer, 1);
assertTrue(watcher.hasIndex(0));
}
@Test
public void testDeepClone() {
WrappedDataWatcher watcher = new WrappedDataWatcher();
watcher.setObject(0, Registry.get(Integer.class), 1);
WrappedDataWatcher cloned = watcher.deepClone();
assertEquals(1, cloned.asMap().size());
assertEquals(1, (Object) cloned.getInteger(0));
}
}
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2016 dmulloy2
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.util.UUID;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import net.minecraft.world.entity.projectile.EntityEgg;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEgg;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author dmulloy2
*/
public class WrappedDataWatcherTest {
@BeforeAll
public static void prepare() {
BukkitInitialization.initializeAll();
}
@Test
public void testBytes() {
// Create a fake lightning strike and get its watcher
EntityEgg nmsEgg = new EntityEgg(null, 0, 0, 0);
CraftEntity craftEgg = new CraftEgg(null, nmsEgg);
WrappedDataWatcher wrapper = WrappedDataWatcher.getEntityWatcher(craftEgg);
WrappedWatchableObject watchable = wrapper.getWatchableObject(0);
WrappedDataWatcherObject object = watchable.getWatcherObject();
// Make sure the serializers work
assertEquals(object.getSerializer(), Registry.get(Byte.class));
// Make sure we can set existing objects
wrapper.setObject(0, (byte) 21);
assertEquals(21, (byte) wrapper.getByte(0));
}
@Test
public void testStrings() {
WrappedDataWatcher wrapper = new WrappedDataWatcher();
// Make sure we can create watcher objects
Serializer serializer = Registry.get(String.class);
WrappedDataWatcherObject object = new WrappedDataWatcherObject(3, serializer);
wrapper.setObject(object, "Test");
assertEquals(wrapper.getString(3), "Test");
}
@Test
public void testFloats() {
WrappedDataWatcher wrapper = new WrappedDataWatcher();
// Make sure we can add new entries
Serializer serializer = Registry.get(Float.class);
WrappedDataWatcherObject object = new WrappedDataWatcherObject(10, serializer);
wrapper.setObject(object, 21.0F);
assertTrue(wrapper.hasIndex(10));
}
@Test
public void testSerializers() {
Serializer blockPos = Registry.get(net.minecraft.core.BlockPosition.class, false);
Serializer optionalBlockPos = Registry.get(net.minecraft.core.BlockPosition.class, true);
assertNotSame(blockPos, optionalBlockPos);
// assertNull(Registry.get(ItemStack.class, false));
assertNotNull(Registry.get(UUID.class, true));
}
@Test
public void testHasIndex() {
WrappedDataWatcher watcher = new WrappedDataWatcher();
Serializer serializer = Registry.get(Integer.class);
assertFalse(watcher.hasIndex(0));
watcher.setObject(0, serializer, 1);
assertTrue(watcher.hasIndex(0));
}
@Test
public void testDeepClone() {
WrappedDataWatcher watcher = new WrappedDataWatcher();
watcher.setObject(0, Registry.get(Integer.class), 1);
WrappedDataWatcher cloned = watcher.deepClone();
assertEquals(1, cloned.asMap().size());
assertEquals(1, (Object) cloned.getInteger(0));
}
}
@@ -1,5 +1,9 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Field;
import java.util.BitSet;
import java.util.List;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.FuzzyReflection;
@@ -8,8 +12,7 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import net.minecraft.core.BlockPosition;
import net.minecraft.core.IRegistry;
import net.minecraft.core.IRegistryCustom;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.server.level.WorldServer;
@@ -22,15 +25,12 @@ import net.minecraft.world.level.chunk.ILightAccess;
import net.minecraft.world.level.lighting.LightEngine;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.internal.matchers.apachecommons.ReflectionEquals;
import java.lang.reflect.Field;
import java.util.BitSet;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.when;
/**
* @author Etrayed
*/
@Disabled // todo: figure this registry update out
public class WrappedLevelChunkDataTest {
@BeforeAll
@@ -47,17 +48,17 @@ public class WrappedLevelChunkDataTest {
ILightAccess access = mock(ILightAccess.class);
when(access.c(0, 0)).thenReturn(BlockAccessAir.a);
when(access.p()).thenReturn(BlockAccessAir.a);
when(access.q()).thenReturn(BlockAccessAir.a);
LightEngine engine = new LightEngine(access, true, true);
WorldServer nmsWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
when(nmsWorld.s()).thenReturn(IRegistryCustom.d.get());
//when(nmsWorld.s()).thenReturn();
// TODO: somehow find a way to always call the real code for all LevelHeightAccessor implementations
when(nmsWorld.v_()).thenReturn(256);
when(nmsWorld.ai()).thenReturn(16); // LevelHeightAccessor is mocked and therefore always returns 0, there are further methods like this which might cause errors in the future
when(nmsWorld.l_()).thenReturn(engine);
when(nmsWorld.m_()).thenReturn(engine);
}
private final WorldServer nmsWorld;
@@ -68,7 +69,7 @@ public class WrappedLevelChunkDataTest {
this.nmsWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
this.chunk = new Chunk(nmsWorld, new ChunkCoordIntPair(5, 5));
IBlockData bellData = IRegistry.V.a(new MinecraftKey("bell")).m();
IBlockData bellData = BuiltInRegistries.f.a(new MinecraftKey("bell")).n();
chunk.b(0).a(0, 0, 0, bellData);
chunk.a(new TileEntityBell(BlockPosition.b, bellData));
@@ -76,8 +77,7 @@ public class WrappedLevelChunkDataTest {
@Test
public void testChunkData() {
ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(chunk,
nmsWorld.l_(), null, null, false);
ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(chunk, nmsWorld.m_(), null, null, false);
PacketContainer container = PacketContainer.fromPacket(packet);
Object rawInstance = container.getSpecificModifier(MinecraftReflection.getLevelChunkPacketDataClass()).read(0);
Object virtualInstance = BukkitConverters.getWrappedChunkDataConverter().getGeneric(container.getLevelChunkData().read(0));
@@ -117,8 +117,7 @@ public class WrappedLevelChunkDataTest {
@Test
public void testLightData() {
ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(chunk,
nmsWorld.l_(), null, null, false);
ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(chunk, nmsWorld.m_(), null, null, false);
PacketContainer container = PacketContainer.fromPacket(packet);
randomizeBitSets(container.getSpecificModifier(MinecraftReflection.getLightUpdatePacketDataClass()).read(0));
@@ -1,62 +1,62 @@
package com.comphenix.protocol.wrappers;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Particle.DustOptions;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class WrappedParticleTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void testBlockData() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.BLOCK_CRACK,
WrappedBlockData.createData(Material.LAPIS_BLOCK));
packet.getNewParticles().write(0, before);
WrappedParticle after = packet.getNewParticles().read(0);
assertEquals(before.getParticle(), after.getParticle());
assertEquals(before.getData(), after.getData());
}
@Test
public void testItemStacks() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.ITEM_CRACK, new ItemStack(Material.FLINT_AND_STEEL));
packet.getNewParticles().write(0, before);
WrappedParticle after = packet.getNewParticles().read(0);
assertEquals(before.getParticle(), after.getParticle());
assertItemsEqual((ItemStack) before.getData(), (ItemStack) after.getData());
}
@Test
public void testRedstone() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.REDSTONE, new DustOptions(Color.BLUE, 1));
packet.getNewParticles().write(0, before);
WrappedParticle after = packet.getNewParticles().read(0);
assertEquals(before.getParticle(), after.getParticle());
DustOptions beforeDust = (DustOptions) before.getData();
DustOptions afterDust = (DustOptions) after.getData();
assertEquals(beforeDust.getColor(), afterDust.getColor());
assertEquals(beforeDust.getSize(), afterDust.getSize(), 0);
}
}
package com.comphenix.protocol.wrappers;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Particle.DustOptions;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class WrappedParticleTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void testBlockData() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.BLOCK_CRACK,
WrappedBlockData.createData(Material.LAPIS_BLOCK));
packet.getNewParticles().write(0, before);
WrappedParticle after = packet.getNewParticles().read(0);
assertEquals(before.getParticle(), after.getParticle());
assertEquals(before.getData(), after.getData());
}
@Test
public void testItemStacks() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.ITEM_CRACK, new ItemStack(Material.FLINT_AND_STEEL));
packet.getNewParticles().write(0, before);
WrappedParticle after = packet.getNewParticles().read(0);
assertEquals(before.getParticle(), after.getParticle());
assertItemsEqual((ItemStack) before.getData(), (ItemStack) after.getData());
}
@Test
public void testRedstone() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.REDSTONE, new DustOptions(Color.BLUE, 1));
packet.getNewParticles().write(0, before);
WrappedParticle after = packet.getNewParticles().read(0);
assertEquals(before.getParticle(), after.getParticle());
DustOptions beforeDust = (DustOptions) before.getData();
DustOptions afterDust = (DustOptions) after.getData();
assertEquals(beforeDust.getColor(), afterDust.getColor());
assertEquals(beforeDust.getSize(), afterDust.getSize(), 0);
}
}
@@ -32,14 +32,12 @@ public class WrappedServerPingTest {
serverPing.setVersionName("Minecraft 123");
serverPing.setVersionProtocol(4);
serverPing.setFavicon(tux);
serverPing.setChatPreviewEnabled(true);
serverPing.setEnforceSecureChat(true);
assertEquals(5, serverPing.getPlayersOnline());
assertEquals(10, serverPing.getPlayersMaximum());
assertEquals("Minecraft 123", serverPing.getVersionName());
assertEquals(4, serverPing.getVersionProtocol());
assertTrue(serverPing.isChatPreviewEnabled());
assertTrue(serverPing.isEnforceSecureChat());
assertArrayEquals(original, serverPing.getFavicon().getData());
@@ -1,87 +1,87 @@
/*
* 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.wrappers.nbt;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class NbtFactoryTest {
@BeforeAll
public static void initializeBukkit() throws IllegalAccessException {
BukkitInitialization.initializeAll();
}
@Test
public void testFromStream() {
WrappedCompound compound = WrappedCompound.fromName("tag");
compound.put("name", "Test Testerson");
compound.put("age", 42);
compound.put(NbtFactory.ofList("nicknames", "a", "b", "c"));
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutput test = new DataOutputStream(buffer);
compound.write(test);
ByteArrayInputStream source = new ByteArrayInputStream(buffer.toByteArray());
DataInput input = new DataInputStream(source);
NbtCompound cloned = NbtBinarySerializer.DEFAULT.deserializeCompound(input);
assertEquals(compound.getString("name"), cloned.getString("name"));
assertEquals(compound.getInteger("age"), cloned.getInteger("age"));
assertEquals(compound.getList("nicknames"), cloned.getList("nicknames"));
}
@Test
public void testItemTag() {
ItemStack test = new ItemStack(Items.L);
org.bukkit.inventory.ItemStack craftTest = MinecraftReflection.getBukkitItemStack(test);
NbtCompound compound = NbtFactory.ofCompound("tag");
compound.put("name", "Test Testerson");
compound.put("age", 42);
NbtFactory.setItemTag(craftTest, compound);
assertEquals(compound, NbtFactory.fromItemTag(craftTest));
}
@Test
public void testCreateTags() {
for (NbtType type : NbtType.values()) {
if (type != NbtType.TAG_END) {
NbtFactory.ofWrapper(type, "");
}
}
}
}
/*
* 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.wrappers.nbt;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class NbtFactoryTest {
@BeforeAll
public static void initializeBukkit() throws IllegalAccessException {
BukkitInitialization.initializeAll();
}
@Test
public void testFromStream() {
WrappedCompound compound = WrappedCompound.fromName("tag");
compound.put("name", "Test Testerson");
compound.put("age", 42);
compound.put(NbtFactory.ofList("nicknames", "a", "b", "c"));
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutput test = new DataOutputStream(buffer);
compound.write(test);
ByteArrayInputStream source = new ByteArrayInputStream(buffer.toByteArray());
DataInput input = new DataInputStream(source);
NbtCompound cloned = NbtBinarySerializer.DEFAULT.deserializeCompound(input);
assertEquals(compound.getString("name"), cloned.getString("name"));
assertEquals(compound.getInteger("age"), cloned.getInteger("age"));
assertEquals(compound.getList("nicknames"), cloned.getList("nicknames"));
}
@Test
public void testItemTag() {
ItemStack test = new ItemStack(Items.L);
org.bukkit.inventory.ItemStack craftTest = MinecraftReflection.getBukkitItemStack(test);
NbtCompound compound = NbtFactory.ofCompound("tag");
compound.put("name", "Test Testerson");
compound.put("age", 42);
NbtFactory.setItemTag(craftTest, compound);
assertEquals(compound, NbtFactory.fromItemTag(craftTest));
}
@Test
public void testCreateTags() {
for (NbtType type : NbtType.values()) {
if (type != NbtType.TAG_END) {
NbtFactory.ofWrapper(type, "");
}
}
}
}
@@ -1,38 +1,38 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2016 dmulloy2
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.comphenix.protocol.wrappers.nbt;
import com.comphenix.protocol.BukkitInitialization;
import org.bukkit.block.BlockState;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author dmulloy2
*/
public class TileEntityTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void test() {
// Ensure the read and write methods exist
TileEntityAccessor<BlockState> accessor = new TileEntityAccessor<>();
accessor.findMethods(null, null);
}
}
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2016 dmulloy2
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.comphenix.protocol.wrappers.nbt;
import com.comphenix.protocol.BukkitInitialization;
import org.bukkit.block.BlockState;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author dmulloy2
*/
public class TileEntityTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void test() {
// Ensure the read and write methods exist
TileEntityAccessor<BlockState> accessor = new TileEntityAccessor<>();
accessor.findMethods(null, null);
}
}