mirror of
https://github.com/Flowsqy/ShopChest.git
synced 2024-12-01 01:53:22 +01:00
Merge branch 'nms-submodules' of https://github.com/Flowsqy/ShopChest
This commit is contained in:
commit
bbdefabef0
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
/bin/
|
||||
/target/
|
||||
target/
|
||||
|
||||
/.idea/
|
||||
*.iml
|
||||
|
12
.mvn/local-settings.xml
Normal file
12
.mvn/local-settings.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 http://maven.apache.org/xsd/settings-1.2.0.xsd">
|
||||
<mirrors>
|
||||
<mirror>
|
||||
<id>maven-default-http-blocker</id>
|
||||
<mirrorOf>dummy</mirrorOf>
|
||||
<name>Dummy mirror to override default blocking mirror that blocks http</name>
|
||||
<url>http://0.0.0.0/</url>
|
||||
</mirror>
|
||||
</mirrors>
|
||||
</settings>
|
1
.mvn/maven.config
Normal file
1
.mvn/maven.config
Normal file
@ -0,0 +1 @@
|
||||
--settings ./.mvn/local-settings.xml
|
@ -25,8 +25,9 @@ To use the API, you need to add the following repository and dependency in your
|
||||
You can find the javadoc here: https://epicericee.github.io/ShopChest/javadoc/
|
||||
|
||||
## Build
|
||||
Clone this repository and use ``mvn clean package`` or ``mvn clean install``.
|
||||
After the build succeeded, the ShopChest.jar is found in the ``/target/`` folder.
|
||||
Clone this repository and use ``sh lib/install_local_depedencies.sh`` to import local dependencies.
|
||||
After importation, use ``mvn clean package`` or ``mvn clean install`` to build the project.
|
||||
After the build succeeded, the ShopChest.jar is found in the ``/plugin/target/`` folder.
|
||||
|
||||
## Issues
|
||||
If you find any issues, please provide them in the [Issues Section](https://github.com/EpicEricEE/ShopChest/issues) with a good description of how to reproduce it. If you get any error messages in the console, please also provide them.
|
||||
|
1
lib/install_local_dependencies.sh
Normal file
1
lib/install_local_dependencies.sh
Normal file
@ -0,0 +1 @@
|
||||
mvn -N install:install-file -Dfile=lib/IslandWorld-8.5.jar -DgroupId=pl.gnacik.islandworld -DartifactId=IslandWorld -Dversion=8.5 -Dpackaging=jar -DgeneratePom=true
|
23
nms/interface/pom.xml
Normal file
23
nms/interface/pom.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>ShopChest-parent</artifactId>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<version>1.14.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>shopchest-nms-interface</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,12 @@
|
||||
package de.epiceric.shopchest.nms;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface FakeArmorStand extends FakeEntity {
|
||||
|
||||
void sendData(String name, Iterable<Player> receivers);
|
||||
|
||||
void setLocation(Location location, Iterable<Player> receivers);
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package de.epiceric.shopchest.nms;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface FakeEntity {
|
||||
|
||||
int getEntityId();
|
||||
|
||||
void spawn(UUID uuid, Location location, Iterable<Player> receivers);
|
||||
|
||||
void remove(Iterable<Player> receivers);
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package de.epiceric.shopchest.nms;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public interface FakeItem extends FakeEntity{
|
||||
|
||||
void sendData(ItemStack item, Iterable<Player> receivers);
|
||||
|
||||
void resetVelocity(Iterable<Player> receivers);
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package de.epiceric.shopchest.nms;
|
||||
|
||||
public interface Platform {
|
||||
|
||||
FakeArmorStand createFakeArmorStand();
|
||||
|
||||
FakeItem createFakeItem();
|
||||
|
||||
TextComponentHelper getTextComponentHelper();
|
||||
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package de.epiceric.shopchest.nms;
|
||||
|
||||
import net.md_5.bungee.api.chat.*;
|
||||
import net.md_5.bungee.api.chat.hover.content.Item;
|
||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public interface TextComponentHelper {
|
||||
|
||||
default void sendUpdateMessage(Player player, String updateMessage, String hoverMessage, String downloadUrl){
|
||||
final TextComponent component = new TextComponent();
|
||||
component.setExtra(Arrays.asList(TextComponent.fromLegacyText(updateMessage)));
|
||||
component.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(hoverMessage)));
|
||||
component.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, downloadUrl));
|
||||
player.spigot().sendMessage(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 'tag' json object containing the item's data
|
||||
* @param itemStack The item stack that will be displayed
|
||||
* @return A string representing a json object of the 'tag'
|
||||
*/
|
||||
String getNbt(ItemStack itemStack);
|
||||
|
||||
default Consumer<Player> getSendableItemInfo(String message, String itemPlaceHolder, ItemStack itemStack, String productName){
|
||||
final TextComponent baseComponent = new TextComponent();
|
||||
final TextComponent replacement = new TextComponent(productName);
|
||||
replacement.setHoverEvent(new HoverEvent(
|
||||
HoverEvent.Action.SHOW_ITEM,
|
||||
new Item(
|
||||
itemStack.getType().getKey().toString(),
|
||||
1,
|
||||
ItemTag.ofNbt(getNbt(itemStack))
|
||||
)
|
||||
));
|
||||
final List<BaseComponent> extras = new ArrayList<>();
|
||||
final Matcher matcher = Pattern.compile(itemPlaceHolder, Pattern.LITERAL).matcher(message);
|
||||
if (matcher.find()) {
|
||||
int cursor = 0;
|
||||
do {
|
||||
final String pre = message.substring(cursor, matcher.start());
|
||||
if (!pre.isEmpty()) {
|
||||
extras.addAll(Arrays.asList(TextComponent.fromLegacyText(pre)));
|
||||
}
|
||||
extras.add(copyPreviousFormatting(extras, replacement));
|
||||
cursor = matcher.end();
|
||||
} while (matcher.find());
|
||||
final String end = message.substring(cursor);
|
||||
if (!end.isEmpty()) {
|
||||
TextComponent endBaseComponent = new TextComponent();
|
||||
endBaseComponent = copyPreviousFormatting(extras, endBaseComponent);
|
||||
endBaseComponent.setExtra(Arrays.asList(TextComponent.fromLegacyText(end)));
|
||||
extras.add(endBaseComponent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
extras.addAll(Arrays.asList(TextComponent.fromLegacyText(message)));
|
||||
}
|
||||
baseComponent.setExtra(extras);
|
||||
|
||||
return player -> player.spigot().sendMessage(baseComponent);
|
||||
}
|
||||
|
||||
static TextComponent copyPreviousFormatting(List<BaseComponent> extras, TextComponent replacement){
|
||||
TextComponent formattedReplacement = replacement;
|
||||
if(!extras.isEmpty()) {
|
||||
formattedReplacement = replacement.duplicate();
|
||||
final BaseComponent previousComponent = extras.get(extras.size() - 1);
|
||||
// Check parent also (not done in copyFormatting)
|
||||
if (formattedReplacement.getColorRaw() == null) {
|
||||
formattedReplacement.setColor(previousComponent.getColor());
|
||||
}
|
||||
if (formattedReplacement.getFontRaw() == null) {
|
||||
formattedReplacement.setFont(previousComponent.getFont());
|
||||
}
|
||||
if (formattedReplacement.isBoldRaw() == null) {
|
||||
formattedReplacement.setBold(previousComponent.isBold());
|
||||
}
|
||||
if (formattedReplacement.isItalicRaw() == null) {
|
||||
formattedReplacement.setItalic(previousComponent.isItalic());
|
||||
}
|
||||
if (formattedReplacement.isUnderlinedRaw() == null) {
|
||||
formattedReplacement.setUnderlined(previousComponent.isUnderlined());
|
||||
}
|
||||
if (formattedReplacement.isStrikethroughRaw() == null) {
|
||||
formattedReplacement.setStrikethrough(previousComponent.isStrikethrough());
|
||||
}
|
||||
if (formattedReplacement.isObfuscatedRaw() == null) {
|
||||
formattedReplacement.setObfuscated(previousComponent.isObfuscated());
|
||||
}
|
||||
if (formattedReplacement.getInsertion() == null) {
|
||||
formattedReplacement.setInsertion(previousComponent.getInsertion());
|
||||
}
|
||||
}
|
||||
return formattedReplacement;
|
||||
}
|
||||
|
||||
}
|
33
nms/reflection/pom.xml
Normal file
33
nms/reflection/pom.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>ShopChest-parent</artifactId>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<version>1.14.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>shopchest-nms-reflection</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<artifactId>shopchest-nms-interface</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.inventivetalent</groupId>
|
||||
<artifactId>reflectionhelper</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,80 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeArmorStand;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FakeArmorStandImpl extends FakeEntityImpl implements FakeArmorStand {
|
||||
|
||||
private final Class<?> packetPlayOutEntityTeleportClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityTeleport");
|
||||
|
||||
public FakeArmorStandImpl(ShopChestDebug debug) {
|
||||
super(debug);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(String name, Iterable<Player> receivers) {
|
||||
Object dataWatcher = ReflectionUtils.createDataWatcher(debug, name, null);
|
||||
try {
|
||||
for (Player receiver : receivers) {
|
||||
ReflectionUtils.sendPacket(debug, packetPlayOutEntityMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class)
|
||||
.newInstance(entityId, dataWatcher, true), receiver);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
debug.getLogger().severe("Could not set hologram data");
|
||||
debug.debug("Could not set armor stand data");
|
||||
debug.debug(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(Location location, Iterable<Player> receivers) {
|
||||
double y = location.getY() + (ReflectionUtils.getServerVersion().equals("v1_8_R1") ? 0 : 1.975);
|
||||
try {
|
||||
Object packet = packetPlayOutEntityTeleportClass.getConstructor().newInstance();
|
||||
Field[] fields = packetPlayOutEntityTeleportClass.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
|
||||
boolean isPre9 = ReflectionUtils.getMajorVersion() < 9;
|
||||
fields[0].set(packet, entityId);
|
||||
|
||||
if (isPre9) {
|
||||
fields[1].set(packet, (int)(location.getX() * 32));
|
||||
fields[2].set(packet, (int)(y * 32));
|
||||
fields[3].set(packet, (int)(location.getZ() * 32));
|
||||
} else {
|
||||
fields[1].set(packet, location.getX());
|
||||
fields[2].set(packet, y);
|
||||
fields[3].set(packet, location.getZ());
|
||||
}
|
||||
fields[4].set(packet, (byte) 0);
|
||||
fields[5].set(packet, (byte) 0);
|
||||
fields[6].set(packet, true);
|
||||
|
||||
if (packet == null) {
|
||||
debug.getLogger().severe("Could not set hologram location");
|
||||
debug.debug("Could not set armor stand location: Packet is null");
|
||||
return;
|
||||
}
|
||||
|
||||
for (Player receiver : receivers) {
|
||||
ReflectionUtils.sendPacket(debug, packet, receiver);
|
||||
}
|
||||
}catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn(UUID uuid, Location location, Iterable<Player> receivers) {
|
||||
for(Player receiver : receivers) {
|
||||
ReflectionUtils.sendPacket(debug, ReflectionUtils.createPacketSpawnEntity(debug, entityId, uuid, location, EntityType.ARMOR_STAND), receiver);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
|
||||
|
||||
public abstract class FakeEntityImpl implements FakeEntity {
|
||||
|
||||
protected final NMSClassResolver nmsClassResolver = new NMSClassResolver();
|
||||
protected final Class<?> packetPlayOutEntityDestroyClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityDestroy");
|
||||
protected final Class<?> packetPlayOutEntityMetadataClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityMetadata");
|
||||
protected final Class<?> dataWatcherClass = nmsClassResolver.resolveSilent("network.syncher.DataWatcher");
|
||||
|
||||
protected final int entityId;
|
||||
protected final ShopChestDebug debug;
|
||||
|
||||
public FakeEntityImpl(ShopChestDebug debug) {
|
||||
this.entityId = ReflectionUtils.getFreeEntityId();
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Iterable<Player> receivers) {
|
||||
try {
|
||||
for(Player receiver : receivers) {
|
||||
ReflectionUtils.sendPacket(debug, packetPlayOutEntityDestroyClass.getConstructor(int[].class).newInstance((Object) new int[]{entityId}), receiver);
|
||||
}
|
||||
} catch (ReflectiveOperationException e){
|
||||
debug.getLogger().severe("Could not remove hologram");
|
||||
debug.debug("Could not remove hologram");
|
||||
debug.debug(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeItem;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.inventivetalent.reflection.resolver.minecraft.OBCClassResolver;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class FakeItemImpl extends FakeEntityImpl implements FakeItem {
|
||||
|
||||
private final OBCClassResolver obcClassResolver = new OBCClassResolver();
|
||||
private final Class<?> packetPlayOutEntityVelocityClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityVelocity");
|
||||
private final Class<?> vec3dClass = nmsClassResolver.resolveSilent("world.phys.Vec3D");
|
||||
private final Class<?> craftItemStackClass = obcClassResolver.resolveSilent("inventory.CraftItemStack");
|
||||
|
||||
public FakeItemImpl(ShopChestDebug debug) {
|
||||
super(debug);
|
||||
|
||||
Class<?> nmsItemStackClass = nmsClassResolver.resolveSilent("world.item.ItemStack");
|
||||
|
||||
Class<?>[] requiredClasses = new Class<?>[] {
|
||||
nmsItemStackClass, craftItemStackClass, packetPlayOutEntityMetadataClass, dataWatcherClass,
|
||||
packetPlayOutEntityDestroyClass, packetPlayOutEntityVelocityClass,
|
||||
};
|
||||
|
||||
for (Class<?> c : requiredClasses) {
|
||||
if (c == null) {
|
||||
debug.debug("Failed to create shop item: Could not find all required classes");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(ItemStack item, Iterable<Player> receivers) {
|
||||
try {
|
||||
Object nmsItemStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, item);
|
||||
Object dataWatcher = ReflectionUtils.createDataWatcher(debug, null, nmsItemStack);
|
||||
for (Player receiver : receivers) {
|
||||
ReflectionUtils.sendPacket(debug, packetPlayOutEntityMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class).newInstance(entityId, dataWatcher, true), receiver);
|
||||
}
|
||||
}catch (ReflectiveOperationException e){
|
||||
debug.getLogger().severe("Failed to send item's data!");
|
||||
debug.debug("Failed to send item's data!");
|
||||
debug.debug(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetVelocity(Iterable<Player> receivers) {
|
||||
try{
|
||||
Object velocityPacket;
|
||||
if (ReflectionUtils.getMajorVersion() < 14) {
|
||||
velocityPacket = packetPlayOutEntityVelocityClass.getConstructor(int.class, double.class, double.class, double.class).newInstance(entityId, 0D, 0D, 0D);
|
||||
} else {
|
||||
Object vec3d = vec3dClass.getConstructor(double.class, double.class, double.class).newInstance(0D, 0D, 0D);
|
||||
velocityPacket = packetPlayOutEntityVelocityClass.getConstructor(int.class, vec3dClass).newInstance(entityId, vec3d);
|
||||
}
|
||||
for(Player receiver : receivers) {
|
||||
ReflectionUtils.sendPacket(debug, velocityPacket, receiver);
|
||||
}
|
||||
}catch (ReflectiveOperationException e){
|
||||
debug.getLogger().severe("Failed to reset item's velocity!");
|
||||
debug.debug("Failed to reset item's velocity!");
|
||||
debug.debug(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn(UUID uuid, Location location, Iterable<Player> receivers) {
|
||||
for(Player receiver : receivers) {
|
||||
ReflectionUtils.sendPacket(debug, ReflectionUtils.createPacketSpawnEntity(debug, entityId, uuid, location, EntityType.DROPPED_ITEM), receiver);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +1,13 @@
|
||||
package de.epiceric.shopchest.nms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.inventivetalent.reflection.resolver.FieldResolver;
|
||||
import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
|
||||
|
||||
import de.epiceric.shopchest.ShopChest;
|
||||
import de.epiceric.shopchest.utils.Utils;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class JsonBuilder {
|
||||
|
||||
@ -115,7 +106,7 @@ public class JsonBuilder {
|
||||
private static final Pattern HEX_PATTERN = Pattern.compile("(§[a-fA-F0-9]){6}");
|
||||
|
||||
private Part rootPart;
|
||||
private ShopChest plugin;
|
||||
private ShopChestDebug debug;
|
||||
|
||||
private final NMSClassResolver nmsClassResolver = new NMSClassResolver();
|
||||
private Class<?> iChatBaseComponentClass = nmsClassResolver.resolveSilent("network.chat.IChatBaseComponent");
|
||||
@ -123,10 +114,10 @@ public class JsonBuilder {
|
||||
private Class<?> chatSerializerClass = nmsClassResolver.resolveSilent("ChatSerializer", "network.chat.IChatBaseComponent$ChatSerializer");
|
||||
private Class<?> chatMessageTypeClass;
|
||||
|
||||
public JsonBuilder(ShopChest plugin) {
|
||||
this.plugin = plugin;
|
||||
public JsonBuilder(ShopChestDebug debug) {
|
||||
this.debug = debug;
|
||||
|
||||
if (Utils.getMajorVersion() >= 16) {
|
||||
if (ReflectionUtils.getMajorVersion() >= 16) {
|
||||
chatMessageTypeClass = nmsClassResolver.resolveSilent("network.chat.ChatMessageType");
|
||||
}
|
||||
|
||||
@ -136,7 +127,7 @@ public class JsonBuilder {
|
||||
|
||||
for (Class<?> c : requiredClasses) {
|
||||
if (c == null) {
|
||||
plugin.debug("Failed to instantiate JsonBuilder: Could not find all required classes");
|
||||
debug.debug("Failed to instantiate JsonBuilder: Could not find all required classes");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -235,17 +226,17 @@ public class JsonBuilder {
|
||||
public void sendJson(Player p) {
|
||||
try {
|
||||
Object iChatBaseComponent = chatSerializerClass.getMethod("a", String.class).invoke(null, toString());
|
||||
Object packetPlayOutChat = Utils.getMajorVersion() < 16
|
||||
Object packetPlayOutChat = ReflectionUtils.getMajorVersion() < 16
|
||||
? packetPlayOutChatClass.getConstructor(iChatBaseComponentClass).newInstance(iChatBaseComponent)
|
||||
: packetPlayOutChatClass.getConstructor(iChatBaseComponentClass, chatMessageTypeClass, UUID.class)
|
||||
.newInstance(iChatBaseComponent, (new FieldResolver(chatMessageTypeClass)).resolve("CHAT", "a").get(null), UUID.randomUUID());
|
||||
|
||||
Utils.sendPacket(plugin, packetPlayOutChat, p);
|
||||
plugin.debug("Sent JSON: " + toString());
|
||||
ReflectionUtils.sendPacket(debug, packetPlayOutChat, p);
|
||||
debug.debug("Sent JSON: " + toString());
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.getLogger().severe("Failed to send JSON with reflection");
|
||||
plugin.debug("Failed to send JSON with reflection: " + toString());
|
||||
plugin.debug(e);
|
||||
debug.getLogger().severe("Failed to send JSON with reflection");
|
||||
debug.debug("Failed to send JSON with reflection: " + toString());
|
||||
debug.debug(e);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeArmorStand;
|
||||
import de.epiceric.shopchest.nms.FakeItem;
|
||||
import de.epiceric.shopchest.nms.Platform;
|
||||
import de.epiceric.shopchest.nms.TextComponentHelper;
|
||||
|
||||
public class PlatformImpl implements Platform {
|
||||
|
||||
private final ShopChestDebug debug;
|
||||
|
||||
public PlatformImpl(ShopChestDebug debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public FakeArmorStand createFakeArmorStand() {
|
||||
return new FakeArmorStandImpl(debug);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeItem createFakeItem() {
|
||||
return new FakeItemImpl(debug);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextComponentHelper getTextComponentHelper() {
|
||||
return new TextComponentHelperImpl(debug);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,285 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.inventivetalent.reflection.resolver.FieldResolver;
|
||||
import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ReflectionUtils {
|
||||
static NMSClassResolver nmsClassResolver = new NMSClassResolver();
|
||||
static Class<?> entityClass = nmsClassResolver.resolveSilent("world.entity.Entity");
|
||||
static Class<?> entityArmorStandClass = nmsClassResolver.resolveSilent("world.entity.decoration.EntityArmorStand");
|
||||
static Class<?> entityItemClass = nmsClassResolver.resolveSilent("world.entity.item.EntityItem");
|
||||
static Class<?> dataWatcherClass = nmsClassResolver.resolveSilent("network.syncher.DataWatcher");
|
||||
static Class<?> dataWatcherObjectClass = nmsClassResolver.resolveSilent("network.syncher.DataWatcherObject");
|
||||
static Class<?> chatSerializerClass = nmsClassResolver.resolveSilent("ChatSerializer", "network.chat.IChatBaseComponent$ChatSerializer");
|
||||
|
||||
private ReflectionUtils() {}
|
||||
|
||||
/**
|
||||
* Create a NMS data watcher object to send via a {@code PacketPlayOutEntityMetadata} packet.
|
||||
* Gravity will be disabled and the custom name will be displayed if available.
|
||||
* @param customName Custom Name of the entity or {@code null}
|
||||
* @param nmsItemStack NMS ItemStack or {@code null} if armor stand
|
||||
*/
|
||||
public static Object createDataWatcher(ShopChestDebug debug, String customName, Object nmsItemStack) {
|
||||
String version = getServerVersion();
|
||||
int majorVersion = getMajorVersion();
|
||||
|
||||
try {
|
||||
byte entityFlags = nmsItemStack == null ? (byte) 0b100000 : 0; // invisible if armor stand
|
||||
byte armorStandFlags = nmsItemStack == null ? (byte) 0b10000 : 0; // marker (since 1.8_R2)
|
||||
|
||||
Object dataWatcher = dataWatcherClass.getConstructor(entityClass).newInstance((Object) null);
|
||||
if (majorVersion < 9) {
|
||||
if (getRevision() == 1) armorStandFlags = 0; // Marker not supported on 1.8_R1
|
||||
|
||||
Method a = dataWatcherClass.getMethod("a", int.class, Object.class);
|
||||
a.invoke(dataWatcher, 0, entityFlags); // flags
|
||||
a.invoke(dataWatcher, 1, (short) 300); // air ticks (?)
|
||||
a.invoke(dataWatcher, 3, (byte) (customName != null ? 1 : 0)); // custom name visible
|
||||
a.invoke(dataWatcher, 2, customName != null ? customName : ""); // custom name
|
||||
a.invoke(dataWatcher, 4, (byte) 1); // silent
|
||||
a.invoke(dataWatcher, 10, nmsItemStack == null ? armorStandFlags : nmsItemStack); // item / armor stand flags
|
||||
} else {
|
||||
Method register = dataWatcherClass.getMethod("register", dataWatcherObjectClass, Object.class);
|
||||
String[] dataWatcherObjectFieldNames;
|
||||
|
||||
if ("v1_9_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"ax", "ay", "aA", "az", "aB", null, "c", "a"};
|
||||
} else if ("v1_9_R2".equals(version)){
|
||||
dataWatcherObjectFieldNames = new String[] {"ay", "az", "aB", "aA", "aC", null, "c", "a"};
|
||||
} else if ("v1_10_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"aa", "az", "aB", "aA", "aC", "aD", "c", "a"};
|
||||
} else if ("v1_11_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"Z", "az", "aB", "aA", "aC", "aD", "c", "a"};
|
||||
} else if ("v1_12_R1".equals(version) || "v1_12_R2".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"Z", "aA", "aC", "aB", "aD", "aE", "c", "a"};
|
||||
} else if ("v1_13_R1".equals(version) || "v1_13_R2".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"ac", "aD", "aF", "aE", "aG", "aH", "b", "a"};
|
||||
} else if ("v1_14_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"W", "AIR_TICKS", "aA", "az", "aB", "aC", "ITEM", "b"};
|
||||
} else if ("v1_15_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"T", "AIR_TICKS", "aA", "az", "aB", "aC", "ITEM", "b"};
|
||||
} else if ("v1_16_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"T", "AIR_TICKS", "ay", "ax", "az", "aA", "ITEM", "b"};
|
||||
} else if ("v1_16_R2".equals(version) || "v1_16_R3".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"S", "AIR_TICKS", "ar", "aq", "as", "at", "ITEM", "b"};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
Field fEntityFlags = entityClass.getDeclaredField(dataWatcherObjectFieldNames[0]);
|
||||
Field fAirTicks = entityClass.getDeclaredField(dataWatcherObjectFieldNames[1]);
|
||||
Field fNameVisible = entityClass.getDeclaredField(dataWatcherObjectFieldNames[2]);
|
||||
Field fCustomName = entityClass.getDeclaredField(dataWatcherObjectFieldNames[3]);
|
||||
Field fSilent = entityClass.getDeclaredField(dataWatcherObjectFieldNames[4]);
|
||||
Field fNoGravity = majorVersion >= 10 ? entityClass.getDeclaredField(dataWatcherObjectFieldNames[5]) : null;
|
||||
Field fItem = entityItemClass.getDeclaredField(dataWatcherObjectFieldNames[6]);
|
||||
Field fArmorStandFlags = entityArmorStandClass.getDeclaredField(dataWatcherObjectFieldNames[7]);
|
||||
|
||||
fEntityFlags.setAccessible(true);
|
||||
fAirTicks.setAccessible(true);
|
||||
fNameVisible.setAccessible(true);
|
||||
fCustomName.setAccessible(true);
|
||||
fSilent.setAccessible(true);
|
||||
if (majorVersion >= 10) fNoGravity.setAccessible(true);
|
||||
fItem.setAccessible(true);
|
||||
fArmorStandFlags.setAccessible(true);
|
||||
|
||||
register.invoke(dataWatcher, fEntityFlags.get(null), entityFlags);
|
||||
register.invoke(dataWatcher, fAirTicks.get(null), 300);
|
||||
register.invoke(dataWatcher, fNameVisible.get(null), customName != null);
|
||||
register.invoke(dataWatcher, fSilent.get(null), true);
|
||||
if (majorVersion < 13) register.invoke(dataWatcher, fCustomName.get(null), customName != null ? customName : "");
|
||||
|
||||
if (nmsItemStack != null) {
|
||||
register.invoke(dataWatcher, fItem.get(null), majorVersion < 11 ? com.google.common.base.Optional.of(nmsItemStack) : nmsItemStack);
|
||||
} else {
|
||||
register.invoke(dataWatcher, fArmorStandFlags.get(null), armorStandFlags);
|
||||
}
|
||||
|
||||
if (majorVersion >= 10) {
|
||||
register.invoke(dataWatcher, fNoGravity.get(null), true);
|
||||
if (majorVersion >= 13) {
|
||||
if (customName != null) {
|
||||
Object iChatBaseComponent = chatSerializerClass.getMethod("a", String.class).invoke(null, JsonBuilder.parse(customName).toString());
|
||||
register.invoke(dataWatcher, fCustomName.get(null), Optional.of(iChatBaseComponent));
|
||||
} else {
|
||||
register.invoke(dataWatcher, fCustomName.get(null), Optional.empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataWatcher;
|
||||
} catch (InstantiationException | InvocationTargetException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
|
||||
debug.getLogger().severe("Failed to create data watcher!");
|
||||
debug.debug("Failed to create data watcher");
|
||||
debug.debug(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a free entity ID for use in {@link #createPacketSpawnEntity(ShopChestDebug, int, UUID, Location, EntityType)}
|
||||
*
|
||||
* @return The id or {@code -1} if a free entity ID could not be retrieved.
|
||||
*/
|
||||
public static int getFreeEntityId() {
|
||||
try {
|
||||
Field entityCountField = new FieldResolver(entityClass).resolve("entityCount", "b");
|
||||
entityCountField.setAccessible(true);
|
||||
if (entityCountField.getType() == int.class) {
|
||||
int id = entityCountField.getInt(null);
|
||||
entityCountField.setInt(null, id+1);
|
||||
return id;
|
||||
} else if (entityCountField.getType() == AtomicInteger.class) {
|
||||
return ((AtomicInteger) entityCountField.get(null)).incrementAndGet();
|
||||
}
|
||||
|
||||
return -1;
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code PacketPlayOutSpawnEntity} object.
|
||||
* Only {@link EntityType#ARMOR_STAND} and {@link EntityType#DROPPED_ITEM} are supported!
|
||||
*/
|
||||
public static Object createPacketSpawnEntity(ShopChestDebug debug, int id, UUID uuid, Location loc, EntityType type) {
|
||||
try {
|
||||
Class<?> packetPlayOutSpawnEntityClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutSpawnEntity");
|
||||
Class<?> entityTypesClass = nmsClassResolver.resolveSilent("world.entity.EntityTypes");
|
||||
|
||||
boolean isPre9 = getMajorVersion() < 9;
|
||||
boolean isPre14 = getMajorVersion() < 14;
|
||||
|
||||
double y = loc.getY();
|
||||
if (type == EntityType.ARMOR_STAND && !getServerVersion().equals("v1_8_R1")) {
|
||||
// Marker armor stand => lift by normal armor stand height
|
||||
y += 1.975;
|
||||
}
|
||||
|
||||
Object packet = packetPlayOutSpawnEntityClass.getConstructor().newInstance();
|
||||
|
||||
Field[] fields = new Field[12];
|
||||
fields[0] = packetPlayOutSpawnEntityClass.getDeclaredField("a"); // ID
|
||||
fields[1] = packetPlayOutSpawnEntityClass.getDeclaredField("b"); // UUID (Only 1.9+)
|
||||
fields[2] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "b" : "c"); // Loc X
|
||||
fields[3] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "c" : "d"); // Loc Y
|
||||
fields[4] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "d" : "e"); // Loc Z
|
||||
fields[5] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "e" : "f"); // Mot X
|
||||
fields[6] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "f" : "g"); // Mot Y
|
||||
fields[7] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "g" : "h"); // Mot Z
|
||||
fields[8] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "h" : "i"); // Pitch
|
||||
fields[9] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "i" : "j"); // Yaw
|
||||
fields[10] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "j" : "k"); // Type
|
||||
fields[11] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "k" : "l"); // Data
|
||||
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
|
||||
Object entityType = null;
|
||||
if (!isPre14) {
|
||||
entityType = entityTypesClass.getField(type == EntityType.ARMOR_STAND ? "ARMOR_STAND" : "ITEM").get(null);
|
||||
}
|
||||
|
||||
fields[0].set(packet, id);
|
||||
if (!isPre9) fields[1].set(packet, uuid);
|
||||
if (isPre9) {
|
||||
fields[2].set(packet, (int)(loc.getX() * 32));
|
||||
fields[3].set(packet, (int)(y * 32));
|
||||
fields[4].set(packet, (int)(loc.getZ() * 32));
|
||||
} else {
|
||||
fields[2].set(packet, loc.getX());
|
||||
fields[3].set(packet, y);
|
||||
fields[4].set(packet, loc.getZ());
|
||||
}
|
||||
fields[5].set(packet, 0);
|
||||
fields[6].set(packet, 0);
|
||||
fields[7].set(packet, 0);
|
||||
fields[8].set(packet, 0);
|
||||
fields[9].set(packet, 0);
|
||||
if (isPre14) fields[10].set(packet, type == EntityType.ARMOR_STAND ? 78 : 2);
|
||||
else fields[10].set(packet, entityType);
|
||||
fields[11].set(packet, 0);
|
||||
|
||||
return packet;
|
||||
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
|
||||
debug.getLogger().severe("Failed to create packet to spawn entity!");
|
||||
debug.debug("Failed to create packet to spawn entity!");
|
||||
debug.debug(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet to a player
|
||||
* @param debug An instance of the {@link ShopChestDebug} debug instance
|
||||
* @param packet Packet to send
|
||||
* @param player Player to which the packet should be sent
|
||||
* @return {@code true} if the packet was sent, or {@code false} if an exception was thrown
|
||||
*/
|
||||
public static boolean sendPacket(ShopChestDebug debug, Object packet, Player player) {
|
||||
try {
|
||||
if (packet == null) {
|
||||
debug.debug("Failed to send packet: Packet is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
Class<?> packetClass = nmsClassResolver.resolveSilent("network.protocol.Packet");
|
||||
if (packetClass == null) {
|
||||
debug.debug("Failed to send packet: Could not find Packet class");
|
||||
return false;
|
||||
}
|
||||
|
||||
Object nmsPlayer = player.getClass().getMethod("getHandle").invoke(player);
|
||||
Field fConnection = (new FieldResolver(nmsPlayer.getClass())).resolve("playerConnection", "b");
|
||||
Object playerConnection = fConnection.get(nmsPlayer);
|
||||
|
||||
playerConnection.getClass().getMethod("sendPacket", packetClass).invoke(playerConnection, packet);
|
||||
|
||||
return true;
|
||||
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException | InvocationTargetException e) {
|
||||
debug.getLogger().severe("Failed to send packet " + packet.getClass().getName());
|
||||
debug.debug("Failed to send packet " + packet.getClass().getName());
|
||||
debug.debug(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current server version with revision number (e.g. v1_9_R2, v1_10_R1)
|
||||
*/
|
||||
public static String getServerVersion() {
|
||||
String packageName = Bukkit.getServer().getClass().getPackage().getName();
|
||||
|
||||
return packageName.substring(packageName.lastIndexOf('.') + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The revision of the current server version (e.g. <i>2</i> for v1_9_R2, <i>1</i> for v1_10_R1)
|
||||
*/
|
||||
public static int getRevision() {
|
||||
return Integer.parseInt(getServerVersion().substring(getServerVersion().length() - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The major version of the server (e.g. <i>9</i> for 1.9.2, <i>10</i> for 1.10)
|
||||
*/
|
||||
public static int getMajorVersion() {
|
||||
return Integer.parseInt(getServerVersion().split("_")[1]);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class ShopChestDebug {
|
||||
|
||||
private final Logger logger;
|
||||
private final Consumer<String> debugConsumer;
|
||||
private final Consumer<Throwable> throwableConsumer;
|
||||
|
||||
public ShopChestDebug(Logger logger, Consumer<String> debugConsumer, Consumer<Throwable> throwableConsumer) {
|
||||
this.logger = logger;
|
||||
this.debugConsumer = debugConsumer;
|
||||
this.throwableConsumer = throwableConsumer;
|
||||
}
|
||||
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public void debug(String message){
|
||||
debugConsumer.accept(message);
|
||||
}
|
||||
|
||||
public void debug(Throwable e){
|
||||
throwableConsumer.accept(e);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import de.epiceric.shopchest.nms.TextComponentHelper;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
|
||||
import org.inventivetalent.reflection.resolver.minecraft.OBCClassResolver;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TextComponentHelperImpl implements TextComponentHelper {
|
||||
|
||||
private static final Pattern COLOR_CODE_PATTERN = Pattern.compile(".*([§]([a-fA-F0-9]))");
|
||||
private static final Pattern FORMAT_CODE_PATTERN = Pattern.compile(".*([§]([l-oL-OkK]))");
|
||||
|
||||
private final ShopChestDebug debug;
|
||||
|
||||
public TextComponentHelperImpl(ShopChestDebug debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendUpdateMessage(Player player, String updateMessage, String hoverMessage, String downloadUrl) {
|
||||
JsonBuilder jb = new JsonBuilder(debug);
|
||||
Map<String, JsonBuilder.Part> hoverEvent = new HashMap<>();
|
||||
hoverEvent.put("action", new JsonBuilder.Part("show_text"));
|
||||
hoverEvent.put("value", new JsonBuilder.Part(hoverMessage));
|
||||
|
||||
Map<String, JsonBuilder.Part> clickEvent = new HashMap<>();
|
||||
clickEvent.put("action", new JsonBuilder.Part("open_url"));
|
||||
clickEvent.put("value", new JsonBuilder.Part(downloadUrl));
|
||||
|
||||
JsonBuilder.PartMap rootPart = JsonBuilder.parse(updateMessage).toMap();
|
||||
|
||||
rootPart.setValue("hoverEvent", new JsonBuilder.PartMap(hoverEvent));
|
||||
rootPart.setValue("clickEvent", new JsonBuilder.PartMap(clickEvent));
|
||||
|
||||
jb.setRootPart(rootPart);
|
||||
jb.sendJson(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNbt(ItemStack itemStack) {
|
||||
try {
|
||||
OBCClassResolver obcClassResolver = new OBCClassResolver();
|
||||
NMSClassResolver nmsClassResolver = new NMSClassResolver();
|
||||
|
||||
Class<?> craftItemStackClass = obcClassResolver.resolveSilent("inventory.CraftItemStack");
|
||||
Object nmsStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, itemStack);
|
||||
Class<?> nbtTagCompoundClass = nmsClassResolver.resolveSilent("nbt.NBTTagCompound");
|
||||
Object nbtTagCompound = nbtTagCompoundClass.getConstructor().newInstance();
|
||||
nmsStack.getClass().getMethod("save", nbtTagCompoundClass).invoke(nmsStack, nbtTagCompound);
|
||||
return nbtTagCompound.toString();
|
||||
}catch (ReflectiveOperationException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<Player> getSendableItemInfo(String message, String itemPlaceHolder, ItemStack itemStack, String productName) {
|
||||
// Add spaces at start and end, so there will always be a part before and after
|
||||
// the item name after splitting at Placeholder.ITEM_NAME
|
||||
String productString = " " + message + " ";
|
||||
|
||||
String[] parts = productString.split(itemPlaceHolder);
|
||||
String jsonItem = "";
|
||||
JsonBuilder jb = new JsonBuilder(debug);
|
||||
JsonBuilder.PartArray rootArray = new JsonBuilder.PartArray();
|
||||
|
||||
try {
|
||||
jsonItem = new JsonPrimitive(getNbt(itemStack)).toString();
|
||||
} catch (RuntimeException e) {
|
||||
debug.getLogger().severe("Failed to create JSON from item. Product preview will not be available.");
|
||||
debug.debug("Failed to create JSON from item:");
|
||||
debug.debug(e.getCause());
|
||||
jb.setRootPart(new JsonBuilder.Part(productString.replace(itemPlaceHolder, productName)));
|
||||
return jb::sendJson;
|
||||
}
|
||||
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
String part = parts[i];
|
||||
|
||||
// Remove spaces at start and end that were added before
|
||||
if (i == 0 && part.startsWith(" ")) {
|
||||
part = part.substring(1);
|
||||
} else if (i == parts.length - 1 && part.endsWith(" ")) {
|
||||
part = part.substring(0, part.length() - 1);
|
||||
}
|
||||
|
||||
String formatPrefix = "";
|
||||
|
||||
// A color code resets all format codes, so only format codes
|
||||
// after the last color code have to be found.
|
||||
int lastColorGroupEndIndex = 0;
|
||||
|
||||
Matcher colorMatcher = COLOR_CODE_PATTERN.matcher(part);
|
||||
if (colorMatcher.find()) {
|
||||
formatPrefix = colorMatcher.group(1);
|
||||
lastColorGroupEndIndex = colorMatcher.end();
|
||||
}
|
||||
|
||||
Matcher formatMatcher = FORMAT_CODE_PATTERN.matcher(part);
|
||||
while (formatMatcher.find(lastColorGroupEndIndex)) {
|
||||
formatPrefix += formatMatcher.group(1);
|
||||
}
|
||||
|
||||
rootArray.addPart(new JsonBuilder.Part(part));
|
||||
|
||||
if (i < parts.length - 1) {
|
||||
JsonBuilder.PartMap hoverEvent = new JsonBuilder.PartMap();
|
||||
hoverEvent.setValue("action", new JsonBuilder.Part("show_item"));
|
||||
hoverEvent.setValue("value", new JsonBuilder.Part(jsonItem, false));
|
||||
|
||||
JsonBuilder.PartMap itemNameMap = JsonBuilder.parse(formatPrefix + productName).toMap();
|
||||
itemNameMap.setValue("hoverEvent", hoverEvent);
|
||||
|
||||
rootArray.addPart(itemNameMap);
|
||||
}
|
||||
}
|
||||
|
||||
jb.setRootPart(rootArray);
|
||||
return jb::sendJson;
|
||||
}
|
||||
|
||||
}
|
42
nms/v1_17_1_R1/pom.xml
Normal file
42
nms/v1_17_1_R1/pom.xml
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>ShopChest-parent</artifactId>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<version>1.14.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>shopchest-nms-v1_17_1_R1</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<spigot.version>1.17.1-R0.1-SNAPSHOT</spigot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<classifier>remapped-mojang</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<artifactId>shopchest-nms-interface</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>specialsource-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,99 @@
|
||||
package de.epiceric.shopchest.nms.v1_17_1_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeArmorStand;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FakeArmorStandImpl extends FakeEntityImpl<String> implements FakeArmorStand {
|
||||
|
||||
private final static byte INVISIBLE_FLAG = 0b100000;
|
||||
private final static byte MARKER_FLAG = 0b10000;
|
||||
private final static EntityDataAccessor<Byte> DATA_SHARED_FLAGS_ID;
|
||||
private final static EntityDataAccessor<Optional<Component>> DATA_CUSTOM_NAME;
|
||||
private final static EntityDataAccessor<Boolean> DATA_CUSTOM_NAME_VISIBLE;
|
||||
private final static float MARKER_ARMOR_STAND_OFFSET = 1.975f;
|
||||
|
||||
static {
|
||||
try {
|
||||
final Field dataSharedFlagsId = Entity.class.getDeclaredField("Z"); // DATA_SHARED_FLAGS_ID
|
||||
dataSharedFlagsId.setAccessible(true);
|
||||
DATA_SHARED_FLAGS_ID = forceCast(dataSharedFlagsId.get(null));
|
||||
final Field dataCustomNameField = Entity.class.getDeclaredField("aJ"); // DATA_CUSTOM_NAME
|
||||
dataCustomNameField.setAccessible(true);
|
||||
DATA_CUSTOM_NAME = forceCast(dataCustomNameField.get(null));
|
||||
final Field dataCustomNameVisibleField = Entity.class.getDeclaredField("aK"); // DATA_CUSTOM_NAME_VISIBLE
|
||||
dataCustomNameVisibleField.setAccessible(true);
|
||||
DATA_CUSTOM_NAME_VISIBLE = forceCast(dataCustomNameVisibleField.get(null));
|
||||
} catch (ReflectiveOperationException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public FakeArmorStandImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(String name, Iterable<Player> receivers) {
|
||||
sendData(receivers, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EntityType<?> getEntityType() {
|
||||
return EntityType.ARMOR_STAND;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getSpawnOffSet() {
|
||||
return MARKER_ARMOR_STAND_OFFSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDataItemCount() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, String name) {
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_SHARED_FLAGS_ID, INVISIBLE_FLAG));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_CUSTOM_NAME, Optional.ofNullable(
|
||||
Component.Serializer.fromJson(
|
||||
ComponentSerializer.toString(
|
||||
TextComponent.fromLegacyText(name)
|
||||
)
|
||||
)
|
||||
)));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_CUSTOM_NAME_VISIBLE, true));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(ArmorStand.DATA_CLIENT_FLAGS, MARKER_FLAG));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(Location location, Iterable<Player> receivers) {
|
||||
final FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buffer.writeVarInt(entityId);
|
||||
buffer.writeDouble(location.getX());
|
||||
buffer.writeDouble(location.getY() + MARKER_ARMOR_STAND_OFFSET);
|
||||
buffer.writeDouble(location.getZ());
|
||||
buffer.writeByte(0);
|
||||
buffer.writeByte(0);
|
||||
buffer.writeBoolean(false);
|
||||
final ClientboundTeleportEntityPacket positionPacket = new ClientboundTeleportEntityPacket(buffer);
|
||||
sendPacket(positionPacket, receivers);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
package de.epiceric.shopchest.nms.v1_17_1_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeEntity;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public abstract class FakeEntityImpl<T> implements FakeEntity {
|
||||
|
||||
private final static AtomicInteger ENTITY_COUNTER;
|
||||
private final static EntityDataAccessor<Boolean> DATA_NO_GRAVITY;
|
||||
private final static EntityDataAccessor<Boolean> DATA_SILENT;
|
||||
private final static Field packedItemField;
|
||||
|
||||
static {
|
||||
try {
|
||||
final Field entityCounterField = Entity.class.getDeclaredField("b"); // ENTITY_COUNTER
|
||||
entityCounterField.setAccessible(true);
|
||||
ENTITY_COUNTER = (AtomicInteger) entityCounterField.get(null);
|
||||
final Field dataNoGravityField = Entity.class.getDeclaredField("aM"); // DATA_NO_GRAVITY
|
||||
dataNoGravityField.setAccessible(true);
|
||||
DATA_NO_GRAVITY = forceCast(dataNoGravityField.get(null));
|
||||
final Field dataSilentField = Entity.class.getDeclaredField("aL"); // DATA_SILENT
|
||||
dataSilentField.setAccessible(true);
|
||||
DATA_SILENT = forceCast(dataSilentField.get(null));
|
||||
packedItemField = ClientboundSetEntityDataPacket.class.getDeclaredField("b"); // packedItems
|
||||
packedItemField.setAccessible(true);
|
||||
}catch (ReflectiveOperationException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T> T forceCast(Object o){
|
||||
return (T) o;
|
||||
}
|
||||
|
||||
protected final int entityId;
|
||||
|
||||
public FakeEntityImpl() {
|
||||
entityId = ENTITY_COUNTER.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
protected void sendPacket(Packet<?> packet, Iterable<Player> receivers){
|
||||
for(Player receiver : receivers){
|
||||
((CraftPlayer)receiver).getHandle().connection.send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn(UUID uuid, Location location, Iterable<Player> receivers) {
|
||||
final ClientboundAddEntityPacket spawnPacket = new ClientboundAddEntityPacket(
|
||||
entityId,
|
||||
uuid,
|
||||
location.getX(),
|
||||
location.getY() + getSpawnOffSet(),
|
||||
location.getZ(),
|
||||
0f,
|
||||
0f,
|
||||
getEntityType(),
|
||||
0,
|
||||
Vec3.ZERO
|
||||
);
|
||||
sendPacket(spawnPacket, receivers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Iterable<Player> receivers) {
|
||||
final ClientboundRemoveEntitiesPacket removePacket = new ClientboundRemoveEntitiesPacket(entityId);
|
||||
sendPacket(removePacket, receivers);
|
||||
}
|
||||
|
||||
protected void sendData(Iterable<Player> receivers, T data){
|
||||
// Create packet
|
||||
final SynchedEntityData entityData = new SynchedEntityData(null);
|
||||
final ClientboundSetEntityDataPacket dataPacket = new ClientboundSetEntityDataPacket(entityId, entityData, false);
|
||||
final List<SynchedEntityData.DataItem<?>> packedItems = new ArrayList<>(2 + getDataItemCount());
|
||||
|
||||
// Setup data
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_NO_GRAVITY, true));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_SILENT, true));
|
||||
addSpecificData(packedItems, data);
|
||||
|
||||
try {
|
||||
packedItemField.set(dataPacket, packedItems);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
// Send packet
|
||||
sendPacket(dataPacket, receivers);
|
||||
}
|
||||
|
||||
protected abstract EntityType<?> getEntityType();
|
||||
|
||||
protected float getSpawnOffSet(){
|
||||
return 0f;
|
||||
}
|
||||
|
||||
protected abstract int getDataItemCount();
|
||||
|
||||
protected abstract void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, T data);
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package de.epiceric.shopchest.nms.v1_17_1_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeItem;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
public class FakeItemImpl extends FakeEntityImpl<ItemStack> implements FakeItem {
|
||||
|
||||
private final static EntityDataAccessor<net.minecraft.world.item.ItemStack> DATA_ITEM;
|
||||
|
||||
static {
|
||||
try{
|
||||
final Field dataItemField = ItemEntity.class.getDeclaredField("c"); // DATA_ITEM
|
||||
dataItemField.setAccessible(true);
|
||||
DATA_ITEM = forceCast(dataItemField.get(null));
|
||||
}catch (ReflectiveOperationException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public FakeItemImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(ItemStack item, Iterable<Player> receivers) {
|
||||
sendData(receivers, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetVelocity(Iterable<Player> receivers) {
|
||||
final ClientboundSetEntityMotionPacket velocityPacket = new ClientboundSetEntityMotionPacket(entityId, Vec3.ZERO);
|
||||
sendPacket(velocityPacket, receivers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EntityType<?> getEntityType() {
|
||||
return EntityType.ITEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDataItemCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, ItemStack data) {
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_ITEM, CraftItemStack.asNMSCopy(data)));
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package de.epiceric.shopchest.nms.v1_17_1_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeArmorStand;
|
||||
import de.epiceric.shopchest.nms.FakeItem;
|
||||
import de.epiceric.shopchest.nms.Platform;
|
||||
import de.epiceric.shopchest.nms.TextComponentHelper;
|
||||
|
||||
public class PlatformImpl implements Platform {
|
||||
|
||||
@Override
|
||||
public FakeArmorStand createFakeArmorStand() {
|
||||
return new FakeArmorStandImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeItem createFakeItem() {
|
||||
return new FakeItemImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextComponentHelper getTextComponentHelper() {
|
||||
return new TextComponentHelperImpl();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package de.epiceric.shopchest.nms.v1_17_1_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.TextComponentHelper;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class TextComponentHelperImpl implements TextComponentHelper {
|
||||
@Override
|
||||
public String getNbt(ItemStack itemStack) {
|
||||
final Tag tag = CraftItemStack.asNMSCopy(itemStack).save(new CompoundTag()).get("tag");
|
||||
return tag == null ? null : tag.getAsString();
|
||||
}
|
||||
}
|
42
nms/v1_17_R1/pom.xml
Normal file
42
nms/v1_17_R1/pom.xml
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>ShopChest-parent</artifactId>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<version>1.14.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>shopchest-nms-v1_17_R1</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<spigot.version>1.17-R0.1-SNAPSHOT</spigot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<classifier>remapped-mojang</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<artifactId>shopchest-nms-interface</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>specialsource-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,99 @@
|
||||
package de.epiceric.shopchest.nms.v1_17_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeArmorStand;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FakeArmorStandImpl extends FakeEntityImpl<String> implements FakeArmorStand {
|
||||
|
||||
private final static byte INVISIBLE_FLAG = 0b100000;
|
||||
private final static byte MARKER_FLAG = 0b10000;
|
||||
private final static EntityDataAccessor<Byte> DATA_SHARED_FLAGS_ID;
|
||||
private final static EntityDataAccessor<Optional<Component>> DATA_CUSTOM_NAME;
|
||||
private final static EntityDataAccessor<Boolean> DATA_CUSTOM_NAME_VISIBLE;
|
||||
private final static float MARKER_ARMOR_STAND_OFFSET = 1.975f;
|
||||
|
||||
static {
|
||||
try {
|
||||
final Field dataSharedFlagsId = Entity.class.getDeclaredField("Z"); // DATA_SHARED_FLAGS_ID
|
||||
dataSharedFlagsId.setAccessible(true);
|
||||
DATA_SHARED_FLAGS_ID = FakeEntityImpl.forceCast(dataSharedFlagsId.get(null));
|
||||
final Field dataCustomNameField = Entity.class.getDeclaredField("aJ"); // DATA_CUSTOM_NAME
|
||||
dataCustomNameField.setAccessible(true);
|
||||
DATA_CUSTOM_NAME = FakeEntityImpl.forceCast(dataCustomNameField.get(null));
|
||||
final Field dataCustomNameVisibleField = Entity.class.getDeclaredField("aK"); // DATA_CUSTOM_NAME_VISIBLE
|
||||
dataCustomNameVisibleField.setAccessible(true);
|
||||
DATA_CUSTOM_NAME_VISIBLE = FakeEntityImpl.forceCast(dataCustomNameVisibleField.get(null));
|
||||
} catch (ReflectiveOperationException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public FakeArmorStandImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(String name, Iterable<Player> receivers) {
|
||||
sendData(receivers, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EntityType<?> getEntityType() {
|
||||
return EntityType.ARMOR_STAND;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getSpawnOffSet() {
|
||||
return MARKER_ARMOR_STAND_OFFSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDataItemCount() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, String name) {
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_SHARED_FLAGS_ID, INVISIBLE_FLAG));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_CUSTOM_NAME, Optional.ofNullable(
|
||||
Component.Serializer.fromJson(
|
||||
ComponentSerializer.toString(
|
||||
TextComponent.fromLegacyText(name)
|
||||
)
|
||||
)
|
||||
)));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_CUSTOM_NAME_VISIBLE, true));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(ArmorStand.DATA_CLIENT_FLAGS, MARKER_FLAG));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(Location location, Iterable<Player> receivers) {
|
||||
final FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buffer.writeVarInt(entityId);
|
||||
buffer.writeDouble(location.getX());
|
||||
buffer.writeDouble(location.getY() + MARKER_ARMOR_STAND_OFFSET);
|
||||
buffer.writeDouble(location.getZ());
|
||||
buffer.writeByte(0);
|
||||
buffer.writeByte(0);
|
||||
buffer.writeBoolean(false);
|
||||
final ClientboundTeleportEntityPacket positionPacket = new ClientboundTeleportEntityPacket(buffer);
|
||||
sendPacket(positionPacket, receivers);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
package de.epiceric.shopchest.nms.v1_17_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeEntity;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundRemoveEntityPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public abstract class FakeEntityImpl<T> implements FakeEntity {
|
||||
|
||||
private final static AtomicInteger ENTITY_COUNTER;
|
||||
private final static EntityDataAccessor<Boolean> DATA_NO_GRAVITY;
|
||||
private final static EntityDataAccessor<Boolean> DATA_SILENT;
|
||||
private final static Field packedItemField;
|
||||
|
||||
static {
|
||||
try {
|
||||
final Field entityCounterField = Entity.class.getDeclaredField("b"); // ENTITY_COUNTER
|
||||
entityCounterField.setAccessible(true);
|
||||
ENTITY_COUNTER = (AtomicInteger) entityCounterField.get(null);
|
||||
final Field dataNoGravityField = Entity.class.getDeclaredField("aM"); // DATA_NO_GRAVITY
|
||||
dataNoGravityField.setAccessible(true);
|
||||
DATA_NO_GRAVITY = forceCast(dataNoGravityField.get(null));
|
||||
final Field dataSilentField = Entity.class.getDeclaredField("aL"); // DATA_SILENT
|
||||
dataSilentField.setAccessible(true);
|
||||
DATA_SILENT = forceCast(dataSilentField.get(null));
|
||||
packedItemField = ClientboundSetEntityDataPacket.class.getDeclaredField("b"); // packedItems
|
||||
packedItemField.setAccessible(true);
|
||||
}catch (ReflectiveOperationException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T> T forceCast(Object o){
|
||||
return (T) o;
|
||||
}
|
||||
|
||||
protected final int entityId;
|
||||
|
||||
public FakeEntityImpl() {
|
||||
entityId = ENTITY_COUNTER.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
protected void sendPacket(Packet<?> packet, Iterable<Player> receivers){
|
||||
for(Player receiver : receivers){
|
||||
((CraftPlayer)receiver).getHandle().connection.send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn(UUID uuid, Location location, Iterable<Player> receivers) {
|
||||
final ClientboundAddEntityPacket spawnPacket = new ClientboundAddEntityPacket(
|
||||
entityId,
|
||||
uuid,
|
||||
location.getX(),
|
||||
location.getY() + getSpawnOffSet(),
|
||||
location.getZ(),
|
||||
0f,
|
||||
0f,
|
||||
getEntityType(),
|
||||
0,
|
||||
Vec3.ZERO
|
||||
);
|
||||
sendPacket(spawnPacket, receivers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Iterable<Player> receivers) {
|
||||
final ClientboundRemoveEntityPacket removePacket = new ClientboundRemoveEntityPacket(entityId);
|
||||
sendPacket(removePacket, receivers);
|
||||
}
|
||||
|
||||
protected void sendData(Iterable<Player> receivers, T data){
|
||||
// Create packet
|
||||
final SynchedEntityData entityData = new SynchedEntityData(null);
|
||||
final ClientboundSetEntityDataPacket dataPacket = new ClientboundSetEntityDataPacket(entityId, entityData, false);
|
||||
final List<SynchedEntityData.DataItem<?>> packedItems = new ArrayList<>(2 + getDataItemCount());
|
||||
|
||||
// Setup data
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_NO_GRAVITY, true));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_SILENT, true));
|
||||
addSpecificData(packedItems, data);
|
||||
|
||||
try {
|
||||
packedItemField.set(dataPacket, packedItems);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
// Send packet
|
||||
sendPacket(dataPacket, receivers);
|
||||
}
|
||||
|
||||
protected abstract EntityType<?> getEntityType();
|
||||
|
||||
protected float getSpawnOffSet(){
|
||||
return 0f;
|
||||
}
|
||||
|
||||
protected abstract int getDataItemCount();
|
||||
|
||||
protected abstract void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, T data);
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package de.epiceric.shopchest.nms.v1_17_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeItem;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
public class FakeItemImpl extends FakeEntityImpl<ItemStack> implements FakeItem {
|
||||
|
||||
private final static EntityDataAccessor<net.minecraft.world.item.ItemStack> DATA_ITEM;
|
||||
|
||||
static {
|
||||
try{
|
||||
final Field dataItemField = ItemEntity.class.getDeclaredField("c"); // DATA_ITEM
|
||||
dataItemField.setAccessible(true);
|
||||
DATA_ITEM = forceCast(dataItemField.get(null));
|
||||
}catch (ReflectiveOperationException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public FakeItemImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(ItemStack item, Iterable<Player> receivers) {
|
||||
sendData(receivers, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetVelocity(Iterable<Player> receivers) {
|
||||
final ClientboundSetEntityMotionPacket velocityPacket = new ClientboundSetEntityMotionPacket(entityId, Vec3.ZERO);
|
||||
sendPacket(velocityPacket, receivers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EntityType<?> getEntityType() {
|
||||
return EntityType.ITEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDataItemCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, ItemStack data) {
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_ITEM, CraftItemStack.asNMSCopy(data)));
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package de.epiceric.shopchest.nms.v1_17_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeArmorStand;
|
||||
import de.epiceric.shopchest.nms.FakeItem;
|
||||
import de.epiceric.shopchest.nms.Platform;
|
||||
import de.epiceric.shopchest.nms.TextComponentHelper;
|
||||
|
||||
public class PlatformImpl implements Platform {
|
||||
|
||||
@Override
|
||||
public FakeArmorStand createFakeArmorStand() {
|
||||
return new FakeArmorStandImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeItem createFakeItem() {
|
||||
return new FakeItemImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextComponentHelper getTextComponentHelper() {
|
||||
return new TextComponentHelperImpl();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package de.epiceric.shopchest.nms.v1_17_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.TextComponentHelper;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class TextComponentHelperImpl implements TextComponentHelper {
|
||||
@Override
|
||||
public String getNbt(ItemStack itemStack) {
|
||||
final Tag tag = CraftItemStack.asNMSCopy(itemStack).save(new CompoundTag()).get("tag");
|
||||
return tag == null ? null : tag.getAsString();
|
||||
}
|
||||
}
|
43
nms/v1_18_R1/pom.xml
Normal file
43
nms/v1_18_R1/pom.xml
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>ShopChest-parent</artifactId>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<version>1.14.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>shopchest-nms-v1_18_R1</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
|
||||
<properties>
|
||||
<spigot.version>1.18-R0.1-SNAPSHOT</spigot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<classifier>remapped-mojang</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<artifactId>shopchest-nms-interface</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>specialsource-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,99 @@
|
||||
package de.epiceric.shopchest.nms.v1_18_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeArmorStand;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FakeArmorStandImpl extends FakeEntityImpl<String> implements FakeArmorStand {
|
||||
|
||||
private final static byte INVISIBLE_FLAG = 0b100000;
|
||||
private final static byte MARKER_FLAG = 0b10000;
|
||||
private final static EntityDataAccessor<Byte> DATA_SHARED_FLAGS_ID;
|
||||
private final static EntityDataAccessor<Optional<Component>> DATA_CUSTOM_NAME;
|
||||
private final static EntityDataAccessor<Boolean> DATA_CUSTOM_NAME_VISIBLE;
|
||||
private final static float MARKER_ARMOR_STAND_OFFSET = 1.975f;
|
||||
|
||||
static {
|
||||
try {
|
||||
final Field dataSharedFlagsId = Entity.class.getDeclaredField("aa"); // DATA_SHARED_FLAGS_ID
|
||||
dataSharedFlagsId.setAccessible(true);
|
||||
DATA_SHARED_FLAGS_ID = forceCast(dataSharedFlagsId.get(null));
|
||||
final Field dataCustomNameField = Entity.class.getDeclaredField("aL"); // DATA_CUSTOM_NAME
|
||||
dataCustomNameField.setAccessible(true);
|
||||
DATA_CUSTOM_NAME = forceCast(dataCustomNameField.get(null));
|
||||
final Field dataCustomNameVisibleField = Entity.class.getDeclaredField("aM"); // DATA_CUSTOM_NAME_VISIBLE
|
||||
dataCustomNameVisibleField.setAccessible(true);
|
||||
DATA_CUSTOM_NAME_VISIBLE = forceCast(dataCustomNameVisibleField.get(null));
|
||||
} catch (ReflectiveOperationException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public FakeArmorStandImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(String name, Iterable<Player> receivers) {
|
||||
sendData(receivers, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EntityType<?> getEntityType() {
|
||||
return EntityType.ARMOR_STAND;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getSpawnOffSet() {
|
||||
return MARKER_ARMOR_STAND_OFFSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDataItemCount() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, String name) {
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_SHARED_FLAGS_ID, INVISIBLE_FLAG));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_CUSTOM_NAME, Optional.ofNullable(
|
||||
Component.Serializer.fromJson(
|
||||
ComponentSerializer.toString(
|
||||
TextComponent.fromLegacyText(name)
|
||||
)
|
||||
)
|
||||
)));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_CUSTOM_NAME_VISIBLE, true));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(ArmorStand.DATA_CLIENT_FLAGS, MARKER_FLAG));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(Location location, Iterable<Player> receivers) {
|
||||
final FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buffer.writeVarInt(entityId);
|
||||
buffer.writeDouble(location.getX());
|
||||
buffer.writeDouble(location.getY() + MARKER_ARMOR_STAND_OFFSET);
|
||||
buffer.writeDouble(location.getZ());
|
||||
buffer.writeByte(0);
|
||||
buffer.writeByte(0);
|
||||
buffer.writeBoolean(false);
|
||||
final ClientboundTeleportEntityPacket positionPacket = new ClientboundTeleportEntityPacket(buffer);
|
||||
sendPacket(positionPacket, receivers);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
package de.epiceric.shopchest.nms.v1_18_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeEntity;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public abstract class FakeEntityImpl<T> implements FakeEntity {
|
||||
|
||||
private final static AtomicInteger ENTITY_COUNTER;
|
||||
private final static EntityDataAccessor<Boolean> DATA_NO_GRAVITY;
|
||||
private final static EntityDataAccessor<Boolean> DATA_SILENT;
|
||||
private final static Field packedItemField;
|
||||
|
||||
static {
|
||||
try {
|
||||
final Field entityCounterField = Entity.class.getDeclaredField("b"); // ENTITY_COUNTER
|
||||
entityCounterField.setAccessible(true);
|
||||
ENTITY_COUNTER = (AtomicInteger) entityCounterField.get(null);
|
||||
final Field dataNoGravityField = Entity.class.getDeclaredField("aO"); // DATA_NO_GRAVITY
|
||||
dataNoGravityField.setAccessible(true);
|
||||
DATA_NO_GRAVITY = forceCast(dataNoGravityField.get(null));
|
||||
final Field dataSilentField = Entity.class.getDeclaredField("aN"); // DATA_SILENT
|
||||
dataSilentField.setAccessible(true);
|
||||
DATA_SILENT = forceCast(dataSilentField.get(null));
|
||||
packedItemField = ClientboundSetEntityDataPacket.class.getDeclaredField("b"); // packedItems
|
||||
packedItemField.setAccessible(true);
|
||||
}catch (ReflectiveOperationException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T> T forceCast(Object o){
|
||||
return (T) o;
|
||||
}
|
||||
|
||||
protected final int entityId;
|
||||
|
||||
public FakeEntityImpl() {
|
||||
entityId = ENTITY_COUNTER.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
protected void sendPacket(Packet<?> packet, Iterable<Player> receivers){
|
||||
for(Player receiver : receivers){
|
||||
((CraftPlayer)receiver).getHandle().connection.send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn(UUID uuid, Location location, Iterable<Player> receivers) {
|
||||
final ClientboundAddEntityPacket spawnPacket = new ClientboundAddEntityPacket(
|
||||
entityId,
|
||||
uuid,
|
||||
location.getX(),
|
||||
location.getY() + getSpawnOffSet(),
|
||||
location.getZ(),
|
||||
0f,
|
||||
0f,
|
||||
getEntityType(),
|
||||
0,
|
||||
Vec3.ZERO
|
||||
);
|
||||
sendPacket(spawnPacket, receivers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Iterable<Player> receivers) {
|
||||
final ClientboundRemoveEntitiesPacket removePacket = new ClientboundRemoveEntitiesPacket(entityId);
|
||||
sendPacket(removePacket, receivers);
|
||||
}
|
||||
|
||||
protected void sendData(Iterable<Player> receivers, T data){
|
||||
// Create packet
|
||||
final SynchedEntityData entityData = new SynchedEntityData(null);
|
||||
final ClientboundSetEntityDataPacket dataPacket = new ClientboundSetEntityDataPacket(entityId, entityData, false);
|
||||
final List<SynchedEntityData.DataItem<?>> packedItems = new ArrayList<>(2 + getDataItemCount());
|
||||
|
||||
// Setup data
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_NO_GRAVITY, true));
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_SILENT, true));
|
||||
addSpecificData(packedItems, data);
|
||||
|
||||
try {
|
||||
packedItemField.set(dataPacket, packedItems);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
// Send packet
|
||||
sendPacket(dataPacket, receivers);
|
||||
}
|
||||
|
||||
protected abstract EntityType<?> getEntityType();
|
||||
|
||||
protected float getSpawnOffSet(){
|
||||
return 0f;
|
||||
}
|
||||
|
||||
protected abstract int getDataItemCount();
|
||||
|
||||
protected abstract void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, T data);
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package de.epiceric.shopchest.nms.v1_18_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeItem;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
public class FakeItemImpl extends FakeEntityImpl<ItemStack> implements FakeItem {
|
||||
|
||||
private final static EntityDataAccessor<net.minecraft.world.item.ItemStack> DATA_ITEM;
|
||||
|
||||
static {
|
||||
try{
|
||||
final Field dataItemField = ItemEntity.class.getDeclaredField("c"); // DATA_ITEM
|
||||
dataItemField.setAccessible(true);
|
||||
DATA_ITEM = forceCast(dataItemField.get(null));
|
||||
}catch (ReflectiveOperationException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public FakeItemImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(ItemStack item, Iterable<Player> receivers) {
|
||||
sendData(receivers, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetVelocity(Iterable<Player> receivers) {
|
||||
final ClientboundSetEntityMotionPacket velocityPacket = new ClientboundSetEntityMotionPacket(entityId, Vec3.ZERO);
|
||||
sendPacket(velocityPacket, receivers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EntityType<?> getEntityType() {
|
||||
return EntityType.ITEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDataItemCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, ItemStack data) {
|
||||
packedItems.add(new SynchedEntityData.DataItem<>(DATA_ITEM, CraftItemStack.asNMSCopy(data)));
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package de.epiceric.shopchest.nms.v1_18_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeArmorStand;
|
||||
import de.epiceric.shopchest.nms.FakeItem;
|
||||
import de.epiceric.shopchest.nms.Platform;
|
||||
import de.epiceric.shopchest.nms.TextComponentHelper;
|
||||
|
||||
public class PlatformImpl implements Platform {
|
||||
|
||||
@Override
|
||||
public FakeArmorStand createFakeArmorStand() {
|
||||
return new FakeArmorStandImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeItem createFakeItem() {
|
||||
return new FakeItemImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextComponentHelper getTextComponentHelper() {
|
||||
return new TextComponentHelperImpl();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package de.epiceric.shopchest.nms.v1_18_R1;
|
||||
|
||||
import de.epiceric.shopchest.nms.TextComponentHelper;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class TextComponentHelperImpl implements TextComponentHelper {
|
||||
@Override
|
||||
public String getNbt(ItemStack itemStack) {
|
||||
final Tag tag = CraftItemStack.asNMSCopy(itemStack).save(new CompoundTag()).get("tag");
|
||||
return tag == null ? null : tag.getAsString();
|
||||
}
|
||||
}
|
136
plugin/pom.xml
Normal file
136
plugin/pom.xml
Normal file
@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>ShopChest-parent</artifactId>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<version>1.14.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ShopChest</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.MilkBowl</groupId>
|
||||
<artifactId>VaultAPI</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>fr.xephi</groupId>
|
||||
<artifactId>authme</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.plotsquared</groupId>
|
||||
<artifactId>PlotSquared-Core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.rlf.uSkyBlock</groupId>
|
||||
<artifactId>uSkyBlock-API</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.wasteofplastic</groupId>
|
||||
<artifactId>askyblock</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.TechFortress</groupId>
|
||||
<artifactId>GriefPrevention</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.wiefferink</groupId>
|
||||
<artifactId>areashop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>world.bentobox</groupId>
|
||||
<artifactId>bentobox</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.IntellectualSites.PlotSquared</groupId>
|
||||
<artifactId>Core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.TownyAdvanced</groupId>
|
||||
<artifactId>Towny</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>pl.gnacik.islandworld</groupId>
|
||||
<artifactId>IslandWorld</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codemc.worldguardwrapper</groupId>
|
||||
<artifactId>worldguardwrapper</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.inventivetalent</groupId>
|
||||
<artifactId>reflectionhelper</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<artifactId>shopchest-nms-interface</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<artifactId>shopchest-nms-reflection</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<artifactId>shopchest-nms-v1_17_R1</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<artifactId>shopchest-nms-v1_17_1_R1</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<artifactId>shopchest-nms-v1_18_R1</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -3,6 +3,19 @@ package de.epiceric.shopchest;
|
||||
import com.palmergames.bukkit.towny.Towny;
|
||||
import com.plotsquared.core.PlotSquared;
|
||||
import com.wasteofplastic.askyblock.ASkyBlock;
|
||||
import de.epiceric.shopchest.nms.Platform;
|
||||
import de.epiceric.shopchest.nms.reflection.PlatformImpl;
|
||||
import de.epiceric.shopchest.nms.reflection.ShopChestDebug;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.codemc.worldguardwrapper.WorldGuardWrapper;
|
||||
|
||||
import de.epiceric.shopchest.command.ShopCommand;
|
||||
import de.epiceric.shopchest.config.Config;
|
||||
import de.epiceric.shopchest.config.HologramFormat;
|
||||
@ -75,6 +88,7 @@ public class ShopChest extends JavaPlugin {
|
||||
private static ShopChest instance;
|
||||
|
||||
private Config config;
|
||||
private Platform platform;
|
||||
private HologramFormat hologramFormat;
|
||||
private ShopCommand shopCommand;
|
||||
private Economy econ = null;
|
||||
@ -181,14 +195,28 @@ public class ShopChest extends JavaPlugin {
|
||||
case "v1_16_R1":
|
||||
case "v1_16_R2":
|
||||
case "v1_16_R3":
|
||||
platform = new PlatformImpl(new ShopChestDebug(getLogger(), this::debug, this::debug));
|
||||
break;
|
||||
case "v1_17_R1":
|
||||
// Need to have an implementation for 1.17.1 and 1.17 -> Change in the name of EntityDestroyPacket
|
||||
// TODO Check CraftMagicNumbers (And create a dedicated class to load Platform)
|
||||
if(Bukkit.getBukkitVersion().equals("1.17.1-R0.1-SNAPSHOT")){
|
||||
platform = new de.epiceric.shopchest.nms.v1_17_1_R1.PlatformImpl();
|
||||
}
|
||||
else {
|
||||
platform = new de.epiceric.shopchest.nms.v1_17_R1.PlatformImpl();
|
||||
}
|
||||
break;
|
||||
case "v1_18_R1":
|
||||
platform = new de.epiceric.shopchest.nms.v1_18_R1.PlatformImpl();
|
||||
break;
|
||||
default:
|
||||
debug("Server version not officially supported: " + Utils.getServerVersion() + "!");
|
||||
debug("Plugin may still work, but more errors are expected!");
|
||||
//debug("Plugin may still work, but more errors are expected!");
|
||||
getLogger().warning("Server version not officially supported: " + Utils.getServerVersion() + "!");
|
||||
getLogger().warning("Plugin may still work, but more errors are expected!");
|
||||
//getLogger().warning("Plugin may still work, but more errors are expected!");
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
shopUtils = new ShopUtils(this);
|
||||
@ -341,12 +369,9 @@ public class ShopChest extends JavaPlugin {
|
||||
getLogger().info("Using MySQL");
|
||||
database = new MySQL(this);
|
||||
if (Config.databaseMySqlPingInterval > 0) {
|
||||
Bukkit.getScheduler().runTaskTimer(this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (database instanceof MySQL) {
|
||||
((MySQL) database).ping();
|
||||
}
|
||||
Bukkit.getScheduler().runTaskTimer(this, () -> {
|
||||
if (database instanceof MySQL) {
|
||||
((MySQL) database).ping();
|
||||
}
|
||||
}, Config.databaseMySqlPingInterval * 20L, Config.databaseMySqlPingInterval * 20L);
|
||||
}
|
||||
@ -532,6 +557,10 @@ public class ShopChest extends JavaPlugin {
|
||||
return shopCreationThreadPool;
|
||||
}
|
||||
|
||||
public Platform getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
public HologramFormat getHologramFormat() {
|
||||
return hologramFormat;
|
||||
}
|
@ -339,7 +339,7 @@ class ShopCommandExecutor implements CommandExecutor {
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>SHALL ONLY BE CALLED VIA {@link ShopCommand#createShopAfterSelected()}</b>
|
||||
* <b>SHALL ONLY BE CALLED VIA {@link ShopCommand#createShopAfterSelected(Player player, SelectClickType clickType)}</b>
|
||||
*/
|
||||
protected void create2(Player p, SelectClickType selectClickType) {
|
||||
ItemStack itemStack = selectClickType.getItem();
|
@ -1,47 +1,5 @@
|
||||
package de.epiceric.shopchest.listeners;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.codemc.worldguardwrapper.WorldGuardWrapper;
|
||||
import org.codemc.worldguardwrapper.flag.IWrappedFlag;
|
||||
import org.codemc.worldguardwrapper.flag.WrappedState;
|
||||
import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
|
||||
import org.inventivetalent.reflection.resolver.minecraft.OBCClassResolver;
|
||||
|
||||
import de.epiceric.shopchest.ShopChest;
|
||||
import de.epiceric.shopchest.config.Config;
|
||||
import de.epiceric.shopchest.config.Placeholder;
|
||||
@ -55,7 +13,6 @@ import de.epiceric.shopchest.external.PlotSquaredShopFlag;
|
||||
import de.epiceric.shopchest.language.LanguageUtils;
|
||||
import de.epiceric.shopchest.language.Message;
|
||||
import de.epiceric.shopchest.language.Replacement;
|
||||
import de.epiceric.shopchest.nms.JsonBuilder;
|
||||
import de.epiceric.shopchest.shop.Shop;
|
||||
import de.epiceric.shopchest.shop.Shop.ShopType;
|
||||
import de.epiceric.shopchest.shop.ShopProduct;
|
||||
@ -67,12 +24,50 @@ import de.epiceric.shopchest.utils.Permissions;
|
||||
import de.epiceric.shopchest.utils.ShopUtils;
|
||||
import de.epiceric.shopchest.utils.Utils;
|
||||
import fr.xephi.authme.api.v3.AuthMeApi;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import net.milkbowl.vault.economy.EconomyResponse;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.Chest;
|
||||
import org.bukkit.block.Container;
|
||||
import org.bukkit.block.DoubleChest;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.codemc.worldguardwrapper.WorldGuardWrapper;
|
||||
import org.codemc.worldguardwrapper.flag.IWrappedFlag;
|
||||
import org.codemc.worldguardwrapper.flag.WrappedState;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
public class ShopInteractListener implements Listener {
|
||||
private static final Pattern COLOR_CODE_PATTERN = Pattern.compile(".*([§]([a-fA-F0-9]))");
|
||||
private static final Pattern FORMAT_CODE_PATTERN = Pattern.compile(".*([§]([l-oL-OkK]))");
|
||||
|
||||
private ShopChest plugin;
|
||||
private Economy econ;
|
||||
@ -634,8 +629,14 @@ public class ShopInteractListener implements Listener {
|
||||
String vendorString = LanguageUtils.getMessage(Message.SHOP_INFO_VENDOR,
|
||||
new Replacement(Placeholder.VENDOR, vendorName));
|
||||
|
||||
// Make JSON message with item preview
|
||||
Component component = getProductComponent(shop.getProduct());
|
||||
final ShopProduct product = shop.getProduct();
|
||||
Consumer<Player> productMessage = plugin.getPlatform().getTextComponentHelper().getSendableItemInfo(
|
||||
LanguageUtils.getMessage(Message.SHOP_INFO_PRODUCT,
|
||||
new Replacement(Placeholder.AMOUNT, String.valueOf(product.getAmount()))),
|
||||
Placeholder.ITEM_NAME.toString(),
|
||||
product.getItemStack(),
|
||||
product.getLocalizedName()
|
||||
);
|
||||
|
||||
String disabled = LanguageUtils.getMessage(Message.SHOP_INFO_DISABLED);
|
||||
|
||||
@ -654,7 +655,7 @@ public class ShopInteractListener implements Listener {
|
||||
|
||||
executor.sendMessage(" ");
|
||||
if (shop.getShopType() != ShopType.ADMIN) executor.sendMessage(vendorString);
|
||||
executor.sendMessage(component);
|
||||
productMessage.accept(executor);
|
||||
if (shop.getShopType() != ShopType.ADMIN && shop.getBuyPrice() > 0) executor.sendMessage(stock);
|
||||
if (shop.getShopType() != ShopType.ADMIN && shop.getSellPrice() > 0) executor.sendMessage(chestSpace);
|
||||
executor.sendMessage(priceString);
|
||||
@ -662,94 +663,6 @@ public class ShopInteractListener implements Listener {
|
||||
executor.sendMessage(" ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link JsonBuilder} containing the shop info message for the product
|
||||
* in which you can hover the item name to get a preview.
|
||||
* @param product The product of the shop
|
||||
* @return A {@link Component} to send to the {@link Player}
|
||||
*/
|
||||
private Component getProductComponent(ShopProduct product) {
|
||||
// Add spaces at start and end, so there will always be a part before and after
|
||||
// the item name after splitting at Placeholder.ITEM_NAME
|
||||
String productString = " " + LanguageUtils.getMessage(Message.SHOP_INFO_PRODUCT,
|
||||
new Replacement(Placeholder.AMOUNT, String.valueOf(product.getAmount()))) + " ";
|
||||
|
||||
String[] parts = productString.split(Placeholder.ITEM_NAME.toString());
|
||||
String productName = product.getLocalizedName();
|
||||
String jsonItem;
|
||||
|
||||
Component component = Component.empty();
|
||||
|
||||
//JsonBuilder jb = new JsonBuilder(plugin);
|
||||
//JsonBuilder.PartArray rootArray = new JsonBuilder.PartArray();
|
||||
|
||||
/*try {
|
||||
OBCClassResolver obcClassResolver = new OBCClassResolver();
|
||||
NMSClassResolver nmsClassResolver = new NMSClassResolver();
|
||||
|
||||
Class<?> craftItemStackClass = obcClassResolver.resolveSilent("inventory.CraftItemStack");
|
||||
Object nmsStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, product.getItemStack());
|
||||
Class<?> nbtTagCompoundClass = nmsClassResolver.resolveSilent("nbt.NBTTagCompound");
|
||||
Object nbtTagCompound = nbtTagCompoundClass.getConstructor().newInstance();
|
||||
nmsStack.getClass().getMethod("save", nbtTagCompoundClass).invoke(nmsStack, nbtTagCompound);
|
||||
jsonItem = new JsonPrimitive(nbtTagCompound.toString()).toString();
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Failed to create JSON from item. Product preview will not be available.");
|
||||
plugin.debug("Failed to create JSON from item:");
|
||||
plugin.debug(e);
|
||||
jb.setRootPart(new JsonBuilder.Part(productString.replace(Placeholder.ITEM_NAME.toString(), productName)));
|
||||
return jb;
|
||||
}*/
|
||||
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
String part = parts[i];
|
||||
|
||||
// Remove spaces at start and end that were added before
|
||||
if (i == 0 && part.startsWith(" ")) {
|
||||
part = part.substring(1);
|
||||
} else if (i == parts.length - 1 && part.endsWith(" ")) {
|
||||
part = part.substring(0, part.length() - 1);
|
||||
}
|
||||
|
||||
StringBuilder formatPrefix = new StringBuilder();
|
||||
|
||||
// A color code resets all format codes, so only format codes
|
||||
// after the last color code have to be found.
|
||||
int lastColorGroupEndIndex = 0;
|
||||
|
||||
Matcher colorMatcher = COLOR_CODE_PATTERN.matcher(part);
|
||||
if (colorMatcher.find()) {
|
||||
formatPrefix = new StringBuilder(colorMatcher.group(1));
|
||||
lastColorGroupEndIndex = colorMatcher.end();
|
||||
}
|
||||
|
||||
Matcher formatMatcher = FORMAT_CODE_PATTERN.matcher(part);
|
||||
while (formatMatcher.find(lastColorGroupEndIndex)) {
|
||||
formatPrefix.append(formatMatcher.group(1));
|
||||
}
|
||||
|
||||
component = component.append(Component.text(part));
|
||||
|
||||
if (i < parts.length - 1) {
|
||||
// todo this might need to be checked
|
||||
component = component.append(Component.text(formatPrefix + productName).hoverEvent(HoverEvent.showItem(product.getItemStack().getType().getKey(), product.getItemStack().getAmount())));
|
||||
|
||||
/*
|
||||
JsonBuilder.PartMap hoverEvent = new JsonBuilder.PartMap();
|
||||
hoverEvent.setValue("action", new JsonBuilder.Part("show_item"));
|
||||
hoverEvent.setValue("value", new JsonBuilder.Part(jsonItem, false));
|
||||
|
||||
JsonBuilder.PartMap itemNameMap = JsonBuilder.parse(formatPrefix + productName).toMap();
|
||||
itemNameMap.setValue("hoverEvent", hoverEvent);
|
||||
|
||||
rootArray.addPart(itemNameMap);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* A player buys from a shop
|
||||
* @param executor Player, who executed the command and will buy the product
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user