Merge branch 'modular-property-system' into upstream-modular-property-system

# Conflicts:
#	plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java
This commit is contained in:
Pyrbu 2023-08-07 16:03:08 +02:00
commit 4e584b8d46
17 changed files with 431 additions and 75 deletions

@ -202,9 +202,9 @@ public class ZNpcsPlus extends JavaPlugin {
NpcEntryImpl entry = npcRegistry.create("debug_npc_" + i, world, type, new NpcLocation(i * 3, 200, 0, 0, 0)); NpcEntryImpl entry = npcRegistry.create("debug_npc_" + i, world, type, new NpcLocation(i * 3, 200, 0, 0, 0));
entry.setProcessed(true); entry.setProcessed(true);
NpcImpl npc = entry.getNpc(); NpcImpl npc = entry.getNpc();
npc.getHologram().addLineComponent(Component.text("Hello, World!", TextColor.color(255, 0, 0))); npc.getHologram().addTextLineComponent(Component.text("Hello, World!", TextColor.color(255, 0, 0)));
npc.getHologram().addLineComponent(Component.text("Hello, World!", TextColor.color(0, 255, 0))); npc.getHologram().addTextLineComponent(Component.text("Hello, World!", TextColor.color(0, 255, 0)));
npc.getHologram().addLineComponent(Component.text("Hello, World!", TextColor.color(0, 0, 255))); npc.getHologram().addTextLineComponent(Component.text("Hello, World!", TextColor.color(0, 0, 255)));
i++; i++;
} }
} }
@ -214,6 +214,7 @@ public class ZNpcsPlus extends JavaPlugin {
public void onDisable() { public void onDisable() {
NpcApiProvider.unregister(); NpcApiProvider.unregister();
for (Runnable runnable : shutdownTasks) runnable.run(); for (Runnable runnable : shutdownTasks) runnable.run();
shutdownTasks.clear();
PacketEvents.getAPI().terminate(); PacketEvents.getAPI().terminate();
} }
@ -283,11 +284,14 @@ public class ZNpcsPlus extends JavaPlugin {
.addSubcommand("reload", new LoadAllCommand(npcRegistry)) .addSubcommand("reload", new LoadAllCommand(npcRegistry))
.addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry))) .addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry)))
.addSubcommand("holo", new MultiCommand(loadHelpMessage("holo")) .addSubcommand("holo", new MultiCommand(loadHelpMessage("holo"))
.addSubcommand("add", new HoloAddCommand(npcRegistry, textSerializer)) .addSubcommand("add", new HoloAddCommand(npcRegistry))
.addSubcommand("additem", new HoloAddItemCommand(npcRegistry))
.addSubcommand("delete", new HoloDeleteCommand(npcRegistry)) .addSubcommand("delete", new HoloDeleteCommand(npcRegistry))
.addSubcommand("info", new HoloInfoCommand(npcRegistry)) .addSubcommand("info", new HoloInfoCommand(npcRegistry))
.addSubcommand("insert", new HoloInsertCommand(npcRegistry, textSerializer)) .addSubcommand("insert", new HoloInsertCommand(npcRegistry))
.addSubcommand("set", new HoloSetCommand(npcRegistry, textSerializer)) .addSubcommand("insertitem", new HoloInsertItemCommand(npcRegistry))
.addSubcommand("set", new HoloSetCommand(npcRegistry))
.addSubcommand("setitem", new HoloSetItemCommand(npcRegistry))
.addSubcommand("offset", new HoloOffsetCommand(npcRegistry)) .addSubcommand("offset", new HoloOffsetCommand(npcRegistry))
.addSubcommand("refreshdelay", new HoloRefreshDelayCommand(npcRegistry))) .addSubcommand("refreshdelay", new HoloRefreshDelayCommand(npcRegistry)))
.addSubcommand("action", new MultiCommand(loadHelpMessage("action")) .addSubcommand("action", new MultiCommand(loadHelpMessage("action"))

@ -4,22 +4,20 @@ import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler; import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException; import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl; import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.hologram.HologramItem;
import lol.pyr.znpcsplus.npc.NpcEntryImpl; import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl; import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
public class HoloAddCommand implements CommandHandler { public class HoloAddCommand implements CommandHandler {
private final NpcRegistryImpl registry; private final NpcRegistryImpl registry;
private final LegacyComponentSerializer textSerializer;
public HoloAddCommand(NpcRegistryImpl registry, LegacyComponentSerializer textSerializer) { public HoloAddCommand(NpcRegistryImpl registry) {
this.registry = registry; this.registry = registry;
this.textSerializer = textSerializer;
} }
@Override @Override
@ -27,7 +25,13 @@ public class HoloAddCommand implements CommandHandler {
context.setUsage(context.getLabel() + " holo add <id> <text>"); context.setUsage(context.getLabel() + " holo add <id> <text>");
HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram(); HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram();
context.ensureArgsNotEmpty(); context.ensureArgsNotEmpty();
hologram.addLineComponent(textSerializer.deserialize(context.dumpAllArgs())); String in = context.dumpAllArgs();
if (in.toLowerCase().startsWith("item:")) {
if (!HologramItem.ensureValidItemInput(in.substring(5))) {
context.halt(Component.text("The item input is invalid!", NamedTextColor.RED));
}
}
hologram.addLine(in);
context.send(Component.text("NPC line added!", NamedTextColor.GREEN)); context.send(Component.text("NPC line added!", NamedTextColor.GREEN));
} }

@ -0,0 +1,39 @@
package lol.pyr.znpcsplus.commands.hologram;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.List;
public class HoloAddItemCommand implements CommandHandler {
private final NpcRegistryImpl registry;
public HoloAddItemCommand(NpcRegistryImpl registry) {
this.registry = registry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " holo additem <id>");
Player player = context.ensureSenderIsPlayer();
org.bukkit.inventory.ItemStack itemStack = player.getInventory().getItemInHand();
if (itemStack == null) context.halt(Component.text("You must be holding an item!", NamedTextColor.RED));
HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram();
hologram.addItemLineStack(itemStack);
context.send(Component.text("NPC item line added!", NamedTextColor.GREEN));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(registry.getModifiableIds());
return Collections.emptyList();
}
}

@ -4,7 +4,6 @@ import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler; import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException; import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl; import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.hologram.HologramLine;
import lol.pyr.znpcsplus.npc.NpcEntryImpl; import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl; import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@ -26,7 +25,11 @@ public class HoloInfoCommand implements CommandHandler {
NpcEntryImpl entry = context.parse(NpcEntryImpl.class); NpcEntryImpl entry = context.parse(NpcEntryImpl.class);
HologramImpl hologram = entry.getNpc().getHologram(); HologramImpl hologram = entry.getNpc().getHologram();
Component component = Component.text("NPC Hologram Info of ID " + entry.getId() + ":", NamedTextColor.GREEN).appendNewline(); Component component = Component.text("NPC Hologram Info of ID " + entry.getId() + ":", NamedTextColor.GREEN).appendNewline();
for (HologramLine line : hologram.getLines()) component = component.append(line.getText()).appendNewline(); for (int i = 0; i < hologram.getLines().size(); i++) {
component = component.append(Component.text(i + ") ", NamedTextColor.GREEN))
.append(Component.text(hologram.getLine(i), NamedTextColor.WHITE))
.appendNewline();
}
context.send(component); context.send(component);
} }

@ -4,11 +4,11 @@ import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler; import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException; import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl; import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.hologram.HologramItem;
import lol.pyr.znpcsplus.npc.NpcEntryImpl; import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl; import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -16,11 +16,9 @@ import java.util.stream.Stream;
public class HoloInsertCommand implements CommandHandler { public class HoloInsertCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry; private final NpcRegistryImpl npcRegistry;
private final LegacyComponentSerializer componentSerializer;
public HoloInsertCommand(NpcRegistryImpl npcRegistry, LegacyComponentSerializer componentSerializer) { public HoloInsertCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry; this.npcRegistry = npcRegistry;
this.componentSerializer = componentSerializer;
} }
@Override @Override
@ -30,7 +28,13 @@ public class HoloInsertCommand implements CommandHandler {
int line = context.parse(Integer.class); int line = context.parse(Integer.class);
if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED)); if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED));
context.ensureArgsNotEmpty(); context.ensureArgsNotEmpty();
hologram.insertLineComponent(line, componentSerializer.deserialize(context.dumpAllArgs())); String in = context.dumpAllArgs();
if (in.toLowerCase().startsWith("item:")) {
if (!HologramItem.ensureValidItemInput(in.substring(5))) {
context.halt(Component.text("The item input is invalid!", NamedTextColor.RED));
}
}
hologram.insertLine(line, in);
context.send(Component.text("NPC line inserted!", NamedTextColor.GREEN)); context.send(Component.text("NPC line inserted!", NamedTextColor.GREEN));
} }

@ -0,0 +1,45 @@
package lol.pyr.znpcsplus.commands.hologram;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
public class HoloInsertItemCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public HoloInsertItemCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " holo insertitem <id> <line>");
HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram();
int line = context.parse(Integer.class);
if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED));
Player player = context.ensureSenderIsPlayer();
org.bukkit.inventory.ItemStack itemStack = player.getInventory().getItemInHand();
if (itemStack == null) context.halt(Component.text("You must be holding an item!", NamedTextColor.RED));
hologram.insertItemLineStack(line, itemStack);
context.send(Component.text("NPC item line inserted!", NamedTextColor.GREEN));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
if (context.argSize() == 2) return context.suggestStream(Stream.iterate(0, n -> n + 1)
.limit(context.suggestionParse(0, NpcEntryImpl.class).getNpc().getHologram().getLines().size())
.map(String::valueOf));
return Collections.emptyList();
}
}

@ -8,7 +8,6 @@ import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl; import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -16,11 +15,9 @@ import java.util.stream.Stream;
public class HoloSetCommand implements CommandHandler { public class HoloSetCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry; private final NpcRegistryImpl npcRegistry;
private final LegacyComponentSerializer componentSerializer;
public HoloSetCommand(NpcRegistryImpl npcRegistry, LegacyComponentSerializer componentSerializer) { public HoloSetCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry; this.npcRegistry = npcRegistry;
this.componentSerializer = componentSerializer;
} }
@Override @Override
@ -31,7 +28,7 @@ public class HoloSetCommand implements CommandHandler {
if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED)); if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED));
context.ensureArgsNotEmpty(); context.ensureArgsNotEmpty();
hologram.removeLine(line); hologram.removeLine(line);
hologram.insertLineComponent(line, componentSerializer.deserialize(context.dumpAllArgs())); hologram.insertLine(line, context.dumpAllArgs());
context.send(Component.text("NPC line set!", NamedTextColor.GREEN)); context.send(Component.text("NPC line set!", NamedTextColor.GREEN));
} }
@ -42,8 +39,7 @@ public class HoloSetCommand implements CommandHandler {
HologramImpl hologram = context.suggestionParse(0, NpcEntryImpl.class).getNpc().getHologram(); HologramImpl hologram = context.suggestionParse(0, NpcEntryImpl.class).getNpc().getHologram();
if (context.argSize() == 2) return context.suggestStream(Stream.iterate(0, n -> n + 1) if (context.argSize() == 2) return context.suggestStream(Stream.iterate(0, n -> n + 1)
.limit(hologram.getLines().size()).map(String::valueOf)); .limit(hologram.getLines().size()).map(String::valueOf));
if (context.argSize() == 3) return context.suggestLiteral(componentSerializer.serialize( if (context.argSize() == 3) return context.suggestLiteral(hologram.getLine(context.suggestionParse(1, Integer.class)));
hologram.getLineComponent(context.suggestionParse(1, Integer.class))));
} }
return Collections.emptyList(); return Collections.emptyList();
} }

@ -0,0 +1,48 @@
package lol.pyr.znpcsplus.commands.hologram;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
public class HoloSetItemCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public HoloSetItemCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " holo setitem <id> <line>");
HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram();
int line = context.parse(Integer.class);
if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED));
Player player = context.ensureSenderIsPlayer();
org.bukkit.inventory.ItemStack itemStack = player.getInventory().getItemInHand();
if (itemStack == null) context.halt(Component.text("You must be holding an item!", NamedTextColor.RED));
hologram.removeLine(line);
hologram.insertItemLineStack(line, itemStack);
context.send(Component.text("NPC item line set!", NamedTextColor.GREEN));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
if (context.argSize() >= 2) {
HologramImpl hologram = context.suggestionParse(0, NpcEntryImpl.class).getNpc().getHologram();
if (context.argSize() == 2) return context.suggestStream(Stream.iterate(0, n -> n + 1)
.limit(hologram.getLines().size()).map(String::valueOf));
}
return Collections.emptyList();
}
}

@ -82,7 +82,7 @@ public class CitizensImporter implements DataImporter {
world = Bukkit.getWorlds().get(0).getName(); world = Bukkit.getWorlds().get(0).getName();
} }
NpcImpl npc = new NpcImpl(uuid, propertyRegistry, configManager, packetFactory, textSerializer, world, typeRegistry.getByName("armor_stand"), new NpcLocation(0, 0, 0, 0, 0)); NpcImpl npc = new NpcImpl(uuid, propertyRegistry, configManager, packetFactory, textSerializer, world, typeRegistry.getByName("armor_stand"), new NpcLocation(0, 0, 0, 0, 0));
npc.getHologram().addLineComponent(textSerializer.deserialize(name)); npc.getHologram().addTextLineComponent(textSerializer.deserialize(name));
ConfigurationSection traits = npcSection.getConfigurationSection("traits"); ConfigurationSection traits = npcSection.getConfigurationSection("traits");
if (traits != null) { if (traits != null) {
for (String traitName : traits.getKeys(false)) { for (String traitName : traits.getKeys(false)) {

@ -106,7 +106,7 @@ public class ZNpcImporter implements DataImporter {
hologram.setOffset(model.getHologramHeight()); hologram.setOffset(model.getHologramHeight());
for (String raw : model.getHologramLines()) { for (String raw : model.getHologramLines()) {
Component line = textSerializer.deserialize(raw); Component line = textSerializer.deserialize(raw);
hologram.addLineComponent(line); hologram.addTextLineComponent(line);
} }
for (ZNpcsAction action : model.getClickActions()) { for (ZNpcsAction action : model.getClickActions()) {

@ -224,6 +224,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
register(new GlowProperty(packetFactory)); register(new GlowProperty(packetFactory));
register(new SimpleBitsetProperty("fire", 0, 0x01)); register(new SimpleBitsetProperty("fire", 0, 0x01));
register(new SimpleBitsetProperty("invisible", 0, 0x20)); register(new SimpleBitsetProperty("invisible", 0, 0x20));
register(new HoloItemProperty());
linkProperties("glow", "fire", "invisible"); linkProperties("glow", "fire", "invisible");
register(new SimpleBooleanProperty("silent", 4, false, legacyBooleans)); register(new SimpleBooleanProperty("silent", 4, false, legacyBooleans));

@ -0,0 +1,24 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import org.bukkit.entity.Player;
import java.util.Map;
public class HoloItemProperty extends EntityPropertyImpl<ItemStack> {
public HoloItemProperty() {
super("holo_item", null, ItemStack.class);
setPlayerModifiable(false);
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
properties.put(8, newEntityData(8, EntityDataTypes.ITEMSTACK, entity.getProperty(this)));
properties.put(5, newEntityData(5, EntityDataTypes.BOOLEAN, true));
}
}

@ -1,11 +1,13 @@
package lol.pyr.znpcsplus.hologram; package lol.pyr.znpcsplus.hologram;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lol.pyr.znpcsplus.api.hologram.Hologram; import lol.pyr.znpcsplus.api.hologram.Hologram;
import lol.pyr.znpcsplus.config.ConfigManager; import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.packets.PacketFactory; import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.Viewable;
import lol.pyr.znpcsplus.util.NpcLocation; import lol.pyr.znpcsplus.util.NpcLocation;
import lol.pyr.znpcsplus.util.Viewable;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -24,7 +26,7 @@ public class HologramImpl extends Viewable implements Hologram {
private long refreshDelay = -1; private long refreshDelay = -1;
private long lastRefresh = System.currentTimeMillis(); private long lastRefresh = System.currentTimeMillis();
private NpcLocation location; private NpcLocation location;
private final List<HologramLine> lines = new ArrayList<>(); private final List<HologramLine<?>> lines = new ArrayList<>();
public HologramImpl(EntityPropertyRegistryImpl propertyRegistry, ConfigManager configManager, PacketFactory packetFactory, LegacyComponentSerializer textSerializer, NpcLocation location) { public HologramImpl(EntityPropertyRegistryImpl propertyRegistry, ConfigManager configManager, PacketFactory packetFactory, LegacyComponentSerializer textSerializer, NpcLocation location) {
this.propertyRegistry = propertyRegistry; this.propertyRegistry = propertyRegistry;
@ -34,32 +36,59 @@ public class HologramImpl extends Viewable implements Hologram {
this.location = location; this.location = location;
} }
public void addLineComponent(Component line) { public void addTextLineComponent(Component line) {
HologramLine newLine = new HologramLine(propertyRegistry, packetFactory, null, line); HologramText newLine = new HologramText(propertyRegistry, packetFactory, null, line);
lines.add(newLine);
relocateLines(newLine);
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
}
public void addTextLine(String line) {
addTextLineComponent(textSerializer.deserialize(line));
}
public void addItemLineStack(org.bukkit.inventory.ItemStack item) {
addItemLinePEStack(SpigotConversionUtil.fromBukkitItemStack(item));
}
public void addItemLine(String serializedItem) {
addItemLinePEStack(HologramItem.deserialize(serializedItem));
}
public void addItemLinePEStack(ItemStack item) {
HologramItem newLine = new HologramItem(propertyRegistry, packetFactory, null, item);
lines.add(newLine); lines.add(newLine);
relocateLines(newLine); relocateLines(newLine);
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer()); for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
} }
public void addLine(String line) { public void addLine(String line) {
addLineComponent(textSerializer.deserialize(line)); if (line.toLowerCase().startsWith("item:")) {
addItemLine(line.substring(5));
} else {
addTextLine(line);
}
} }
public Component getLineComponent(int index) { public Component getLineTextComponent(int index) {
return lines.get(index).getText(); return ((HologramText) lines.get(index)).getValue();
} }
public String getLine(int index) { public String getLine(int index) {
return textSerializer.serialize(getLineComponent(index)); if (lines.get(index) instanceof HologramItem) {
return ((HologramItem) lines.get(index)).serialize();
} else {
return textSerializer.serialize(getLineTextComponent(index));
}
} }
public void removeLine(int index) { public void removeLine(int index) {
HologramLine line = lines.remove(index); HologramLine<?> line = lines.remove(index);
for (Player viewer : getViewers()) line.hide(viewer); for (Player viewer : getViewers()) line.hide(viewer);
relocateLines(); relocateLines();
} }
public List<HologramLine> getLines() { public List<HologramLine<?>> getLines() {
return Collections.unmodifiableList(lines); return Collections.unmodifiableList(lines);
} }
@ -68,25 +97,48 @@ public class HologramImpl extends Viewable implements Hologram {
lines.clear(); lines.clear();
} }
public void insertLineComponent(int index, Component line) { public void insertTextLineComponent(int index, Component line) {
HologramLine newLine = new HologramLine(propertyRegistry, packetFactory, null, line); HologramText newLine = new HologramText(propertyRegistry, packetFactory, null, line);
lines.add(index, newLine); lines.add(index, newLine);
relocateLines(newLine); relocateLines(newLine);
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer()); for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
} }
public void insertTextLine(int index, String line) {
insertTextLineComponent(index, textSerializer.deserialize(line));
}
public void insertItemLineStack(int index, org.bukkit.inventory.ItemStack item) {
insertItemLinePEStack(index, SpigotConversionUtil.fromBukkitItemStack(item));
}
public void insertItemLinePEStack(int index, ItemStack item) {
HologramItem newLine = new HologramItem(propertyRegistry, packetFactory, null, item);
lines.add(index, newLine);
relocateLines(newLine);
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
}
public void insertItemLine(int index, String item) {
insertItemLinePEStack(index, HologramItem.deserialize(item));
}
public void insertLine(int index, String line) { public void insertLine(int index, String line) {
insertLineComponent(index, textSerializer.deserialize(line)); if (line.toLowerCase().startsWith("item:")) {
insertItemLine(index, line.substring(5));
} else {
insertTextLine(index, line);
}
} }
@Override @Override
protected void UNSAFE_show(Player player) { protected void UNSAFE_show(Player player) {
for (HologramLine line : lines) line.show(player); for (HologramLine<?> line : lines) line.show(player);
} }
@Override @Override
protected void UNSAFE_hide(Player player) { protected void UNSAFE_hide(Player player) {
for (HologramLine line : lines) line.hide(player); for (HologramLine<?> line : lines) line.hide(player);
} }
public long getRefreshDelay() { public long getRefreshDelay() {
@ -103,7 +155,7 @@ public class HologramImpl extends Viewable implements Hologram {
public void refresh() { public void refresh() {
lastRefresh = System.currentTimeMillis(); lastRefresh = System.currentTimeMillis();
for (HologramLine line : lines) for (Player viewer : getViewers()) line.refreshMeta(viewer); for (HologramLine<?> line : lines) for (Player viewer : getViewers()) line.refreshMeta(viewer);
} }
public void setLocation(NpcLocation location) { public void setLocation(NpcLocation location) {
@ -115,10 +167,10 @@ public class HologramImpl extends Viewable implements Hologram {
relocateLines(null); relocateLines(null);
} }
private void relocateLines(HologramLine newLine) { private void relocateLines(HologramLine<?> newLine) {
final double lineSpacing = configManager.getConfig().lineSpacing(); final double lineSpacing = configManager.getConfig().lineSpacing();
double height = location.getY() + (lines.size() - 1) * lineSpacing + getOffset(); double height = location.getY() + (lines.size() - 1) * lineSpacing + getOffset();
for (HologramLine line : lines) { for (HologramLine<?> line : lines) {
line.setLocation(location.withY(height), line == newLine ? Collections.emptySet() : getViewers()); line.setLocation(location.withY(height), line == newLine ? Collections.emptySet() : getViewers());
height -= lineSpacing; height -= lineSpacing;
} }

@ -0,0 +1,111 @@
package lol.pyr.znpcsplus.hologram;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTNumber;
import com.github.retrooper.packetevents.protocol.nbt.codec.NBTCodec;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.NpcLocation;
import org.bukkit.entity.Player;
import java.util.Collection;
public class HologramItem extends HologramLine<ItemStack> {
public HologramItem(EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, ItemStack item) {
super(item, packetFactory, EntityTypes.ITEM, location);
addProperty(propertyRegistry.getByName("holo_item"));
}
@SuppressWarnings("unchecked")
@Override
public <T> T getProperty(EntityProperty<T> key) {
if (key.getName().equalsIgnoreCase("holo_item")) return (T) getValue();
return super.getProperty(key);
}
@Override
public void setLocation(NpcLocation location, Collection<Player> viewers) {
super.setLocation(location.withY(location.getY() + 2.05), viewers);
}
public static boolean ensureValidItemInput(String in) {
if (in == null || in.isEmpty()) {
return false;
}
int indexOfNbt = in.indexOf("{");
if (indexOfNbt != -1) {
String typeName = in.substring(0, indexOfNbt);
ItemType type = ItemTypes.getByName("minecraft:" + typeName.toLowerCase());
if (type == null) {
return false;
}
String nbtString = in.substring(indexOfNbt);
return ensureValidNbt(nbtString);
} else {
ItemType type = ItemTypes.getByName("minecraft:" + in.toLowerCase());
return type != null;
}
}
private static boolean ensureValidNbt(String nbtString) {
JsonElement nbtJson;
try {
nbtJson = JsonParser.parseString(nbtString);
} catch (JsonSyntaxException e) {
return false;
}
try {
NBTCodec.jsonToNBT(nbtJson);
} catch (Exception ignored) {
return false;
}
return true;
}
public static ItemStack deserialize(String serializedItem) {
int indexOfNbt = serializedItem.indexOf("{");
String typeName = serializedItem;
int amount = 1;
NBTCompound nbt = new NBTCompound();
if (indexOfNbt != -1) {
typeName = serializedItem.substring(0, indexOfNbt);
String nbtString = serializedItem.substring(indexOfNbt);
JsonElement nbtJson = null;
try {
nbtJson = JsonParser.parseString(nbtString);
} catch (Exception ignored) {
}
if (nbtJson != null) {
nbt = (NBTCompound) NBTCodec.jsonToNBT(nbtJson);
NBTNumber nbtAmount = nbt.getNumberTagOrNull("Count");
if (nbtAmount != null) {
nbt.removeTag("Count");
amount = nbtAmount.getAsInt();
if (amount <= 0) amount = 1;
if (amount > 127) amount = 127;
}
}
}
ItemType type = ItemTypes.getByName("minecraft:" + typeName.toLowerCase());
if (type == null) type = ItemTypes.STONE;
return ItemStack.builder().type(type).amount(amount).nbt(nbt).build();
}
public String serialize() {
NBTCompound nbt = getValue().getNBT();
if (nbt == null) nbt = new NBTCompound();
if (getValue().getAmount() > 1) nbt.setTag("Count", new NBTInt(getValue().getAmount()));
if (nbt.isEmpty()) return "item:" + getValue().getType().getName().toString().replace("minecraft:", "");
return "item:" + getValue().getType().getName().toString().replace("minecraft:", "") + NBTCodec.nbtToJson(nbt, true);
}
}

@ -1,76 +1,73 @@
package lol.pyr.znpcsplus.hologram; package lol.pyr.znpcsplus.hologram;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import lol.pyr.znpcsplus.api.entity.EntityProperty; import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.PropertyHolder; import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.packets.PacketFactory; import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.NpcLocation; import lol.pyr.znpcsplus.util.NpcLocation;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class HologramLine implements PropertyHolder { public class HologramLine<M> implements PropertyHolder {
private Component text; private M value;
private final PacketEntity armorStand; private final PacketEntity entity;
private final Set<EntityProperty<?>> properties; private final Set<EntityProperty<?>> properties;
public HologramLine(EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, Component text) { public HologramLine(M value, PacketFactory packetFactory, EntityType type, NpcLocation location) {
this.text = text; this.value = value;
this.entity = new PacketEntity(packetFactory, this, type, location);
this.properties = new HashSet<>(); this.properties = new HashSet<>();
this.properties.add(propertyRegistry.getByName("name"));
this.properties.add(propertyRegistry.getByName("invisible"));
armorStand = new PacketEntity(packetFactory, this, EntityTypes.ARMOR_STAND, location);
} }
public Component getText() { public M getValue() {
return text; return value;
} }
public void setText(Component text) { public void setValue(M value) {
this.text = text; this.value = value;
} }
public void refreshMeta(Player player) { public void refreshMeta(Player player) {
armorStand.refreshMeta(player); entity.refreshMeta(player);
} }
protected void show(Player player) { protected void show(Player player) {
armorStand.spawn(player); entity.spawn(player);
} }
protected void hide(Player player) { protected void hide(Player player) {
armorStand.despawn(player); entity.despawn(player);
} }
public void setLocation(NpcLocation location, Collection<Player> viewers) { public void setLocation(NpcLocation location, Collection<Player> viewers) {
armorStand.setLocation(location, viewers); entity.setLocation(location, viewers);
} }
public int getEntityId() { public int getEntityId() {
return armorStand.getEntityId(); return entity.getEntityId();
}
public <T> void addProperty(EntityProperty<T> property) {
properties.add(property);
} }
@SuppressWarnings("unchecked")
@Override @Override
public <T> T getProperty(EntityProperty<T> key) { public <T> T getProperty(EntityProperty<T> key) {
if (key.getName().equalsIgnoreCase("invisible")) return (T) Boolean.TRUE;
if (key.getName().equalsIgnoreCase("name")) return (T) text;
return key.getDefaultValue(); return key.getDefaultValue();
} }
@Override @Override
public boolean hasProperty(EntityProperty<?> key) { public boolean hasProperty(EntityProperty<?> key) {
return key.getName().equalsIgnoreCase("name") || key.getName().equalsIgnoreCase("invisible"); return properties.contains(key);
} }
@Override @Override
public <T> void setProperty(EntityProperty<T> key, T value) { public <T> void setProperty(EntityProperty<T> key, T value) {
throw new UnsupportedOperationException("Can't set properties on a hologram"); throw new UnsupportedOperationException("Can't set properties on a hologram line");
} }
@Override @Override

@ -0,0 +1,30 @@
package lol.pyr.znpcsplus.hologram;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.NpcLocation;
import net.kyori.adventure.text.Component;
public class HologramText extends HologramLine<Component> {
public HologramText(EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, Component text) {
super(text, packetFactory, EntityTypes.ARMOR_STAND, location);
addProperty(propertyRegistry.getByName("name"));
addProperty(propertyRegistry.getByName("invisible"));
}
@SuppressWarnings("unchecked")
@Override
public <T> T getProperty(EntityProperty<T> key) {
if (key.getName().equalsIgnoreCase("invisible")) return (T) Boolean.TRUE;
if (key.getName().equalsIgnoreCase("name")) return (T) getValue();
return super.getProperty(key);
}
@Override
public boolean hasProperty(EntityProperty<?> key) {
return key.getName().equalsIgnoreCase("name") || key.getName().equalsIgnoreCase("invisible");
}
}

@ -6,7 +6,6 @@ import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PropertySerializer; import lol.pyr.znpcsplus.entity.PropertySerializer;
import lol.pyr.znpcsplus.hologram.HologramImpl; import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.hologram.HologramLine;
import lol.pyr.znpcsplus.interaction.ActionRegistry; import lol.pyr.znpcsplus.interaction.ActionRegistry;
import lol.pyr.znpcsplus.npc.NpcEntryImpl; import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcImpl; import lol.pyr.znpcsplus.npc.NpcImpl;
@ -14,7 +13,6 @@ import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
import lol.pyr.znpcsplus.packets.PacketFactory; import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.storage.NpcStorage; import lol.pyr.znpcsplus.storage.NpcStorage;
import lol.pyr.znpcsplus.util.NpcLocation; import lol.pyr.znpcsplus.util.NpcLocation;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
@ -73,7 +71,7 @@ public class YamlStorage implements NpcStorage {
HologramImpl hologram = npc.getHologram(); HologramImpl hologram = npc.getHologram();
hologram.setOffset(config.getDouble("hologram.offset", 0.0)); hologram.setOffset(config.getDouble("hologram.offset", 0.0));
hologram.setRefreshDelay(config.getLong("hologram.refresh-delay", -1)); hologram.setRefreshDelay(config.getLong("hologram.refresh-delay", -1));
for (String line : config.getStringList("hologram.lines")) hologram.addLineComponent(MiniMessage.miniMessage().deserialize(line)); for (String line : config.getStringList("hologram.lines")) hologram.addLine(line);
for (String s : config.getStringList("actions")) npc.addAction(actionRegistry.deserialize(s)); for (String s : config.getStringList("actions")) npc.addAction(actionRegistry.deserialize(s));
NpcEntryImpl entry = new NpcEntryImpl(config.getString("id"), npc); NpcEntryImpl entry = new NpcEntryImpl(config.getString("id"), npc);
@ -112,8 +110,8 @@ public class YamlStorage implements NpcStorage {
if (hologram.getOffset() != 0.0) config.set("hologram.offset", hologram.getOffset()); if (hologram.getOffset() != 0.0) config.set("hologram.offset", hologram.getOffset());
if (hologram.getRefreshDelay() != -1) config.set("hologram.refresh-delay", hologram.getRefreshDelay()); if (hologram.getRefreshDelay() != -1) config.set("hologram.refresh-delay", hologram.getRefreshDelay());
List<String> lines = new ArrayList<>(npc.getHologram().getLines().size()); List<String> lines = new ArrayList<>(npc.getHologram().getLines().size());
for (HologramLine line : hologram.getLines()) { for (int i = 0; i < hologram.getLines().size(); i++) {
lines.add(MiniMessage.miniMessage().serialize(line.getText())); lines.add(hologram.getLine(i));
} }
config.set("hologram.lines", lines); config.set("hologram.lines", lines);
config.set("actions", npc.getActions().stream() config.set("actions", npc.getActions().stream()