diff --git a/api/src/main/java/lol/pyr/znpcsplus/util/Vector3i.java b/api/src/main/java/lol/pyr/znpcsplus/util/Vector3i.java new file mode 100644 index 0000000..ae6a933 --- /dev/null +++ b/api/src/main/java/lol/pyr/znpcsplus/util/Vector3i.java @@ -0,0 +1,46 @@ +package lol.pyr.znpcsplus.util; + +public class Vector3i { + private final int x; + private final int y; + private final int z; + + public Vector3i(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public int getX() { + return this.x; + } + + public int getY() { + return this.y; + } + + public int getZ() { + return this.z; + } + + public String toString() { + return this.x + "," + this.y + "," + this.z; + } + + public String toPrettyString() { + return "(" + this.x + ", " + this.y + ", " + this.z + ")"; + } + + public static Vector3i fromString(String s) { + String[] split = s.split(","); + if (split.length < 3) { + return null; + } else { + try { + return new Vector3i(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2])); + } catch (NumberFormatException var3) { + return null; + } + } + } +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java index e076bc5..fe06753 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java @@ -251,6 +251,7 @@ public class ZNpcsPlus extends JavaPlugin { manager.registerParser(Color.class, new ColorParser(incorrectUsageMessage)); manager.registerParser(Vector3f.class, new Vector3fParser(incorrectUsageMessage)); manager.registerParser(String.class, new StringParser(incorrectUsageMessage)); + manager.registerParser(Vector3i.class, new Vector3iParser(incorrectUsageMessage)); // TODO: Need to find a better way to do this registerEnumParser(manager, NpcPose.class, incorrectUsageMessage); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/commands/property/PropertySetCommand.java b/plugin/src/main/java/lol/pyr/znpcsplus/commands/property/PropertySetCommand.java index 0380025..d5ff9dd 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/commands/property/PropertySetCommand.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/commands/property/PropertySetCommand.java @@ -19,6 +19,9 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Color; import com.github.retrooper.packetevents.protocol.item.ItemStack; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; import java.util.Arrays; import java.util.Collections; @@ -117,9 +120,20 @@ public class PropertySetCommand implements CommandHandler { value = context.parse(type); valueName = value == null ? "NONE" : ((NpcEntryImpl) value).getId(); } - else { + else if (type == Vector3i.class) { value = context.parse(type); - valueName = String.valueOf(value); + valueName = value == null ? "NONE" : ((Vector3i) value).toPrettyString(); + } + else { + try { + value = context.parse(type); + valueName = String.valueOf(value); + } catch (NullPointerException e) { + context.send(Component.text("An error occurred while trying to parse the value. Please report this to the plugin author.", + NamedTextColor.RED)); + e.printStackTrace(); + return; + } } npc.UNSAFE_setProperty(property, value); @@ -144,6 +158,17 @@ public class PropertySetCommand implements CommandHandler { context.suggestEnum(Arrays.stream(SpellType.values()).filter(spellType -> spellType.ordinal() <= 3).toArray(SpellType[]::new)) : context.suggestEnum(SpellType.values()); + if (type == Vector3i.class) { + if (context.getSender() instanceof Player) { + Player player = (Player) context.getSender(); + Block targetBlock = player.getTargetBlock(Collections.singleton(Material.AIR), 5); + if (targetBlock.getType().equals(Material.AIR)) return Collections.emptyList(); + return context.suggestLiteral( + targetBlock.getX() + "", + targetBlock.getX() + " " + targetBlock.getY(), + targetBlock.getX() + " " + targetBlock.getY() + " " + targetBlock.getZ()); + } + } // Suggest enum values directly if (type.isEnum()) { return context.suggestEnum((Enum[]) type.getEnumConstants()); @@ -154,6 +179,25 @@ public class PropertySetCommand implements CommandHandler { // TODO: suggest block with nbt like minecraft setblock command return context.suggestionParse(2, String.class).equals("block") ? context.suggestStream(StateTypes.values().stream().map(StateType::getName)) : Collections.emptyList(); } + if (type == Vector3i.class) { + if (context.getSender() instanceof Player) { + Player player = (Player) context.getSender(); + Block targetBlock = player.getTargetBlock(Collections.singleton(Material.AIR), 5); + if (targetBlock.getType().equals(Material.AIR)) return Collections.emptyList(); + return context.suggestLiteral( + targetBlock.getY() + "", + targetBlock.getY() + " " + targetBlock.getZ()); + } + } + } else if (context.argSize() == 5) { + if (type == Vector3i.class) { + if (context.getSender() instanceof Player) { + Player player = (Player) context.getSender(); + Block targetBlock = player.getTargetBlock(Collections.singleton(Material.AIR), 5); + if (targetBlock.getType().equals(Material.AIR)) return Collections.emptyList(); + return context.suggestLiteral(targetBlock.getZ() + ""); + } + } } } return Collections.emptyList(); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java index 08ede74..7bd54bf 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java @@ -56,6 +56,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { registerSerializer(new Vector3fPropertySerializer()); registerSerializer(new BlockStatePropertySerializer()); registerSerializer(new LookTypeSerializer()); + registerSerializer(new GenericSerializer<>(Vector3i::toString, Vector3i::fromString, Vector3i.class)); registerEnumSerializer(NpcPose.class); registerEnumSerializer(DyeColor.class); @@ -251,6 +252,17 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { register(new BitsetProperty("is_rearing", horseIndex, horseEating << 1, false, legacyBooleans)); register(new BitsetProperty("has_mouth_open", horseIndex, horseEating << 2, false, legacyBooleans)); + // End Crystal + if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) { + int endCrystalIndex; + if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) endCrystalIndex = 8; + else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) endCrystalIndex = 7; + else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) endCrystalIndex = 6; + else endCrystalIndex = 5; + register(new OptionalBlockPosProperty("beam_target", null, endCrystalIndex++)); + register(new BooleanProperty("show_base", endCrystalIndex, true, false)); + } + // Horse if (ver.isNewerThanOrEquals(ServerVersion.V_1_8) && ver.isOlderThan(ServerVersion.V_1_9)) { register(new EncodedByteProperty<>("horse_type", HorseType.HORSE, 19, obj -> (byte) obj.ordinal())); @@ -367,7 +379,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) witherIndex = 11; else witherIndex = 17; witherIndex += 3; // skip the first 3 indexes, will be used for the other properties later - register(new IntegerProperty("invulnerable_time", witherIndex++, 0, false)); + register(new IntegerProperty("invulnerable_time", witherIndex, 0, false)); if (!ver.isNewerThanOrEquals(ServerVersion.V_1_9)) return; // Shulker diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/OptionalBlockPosProperty.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/OptionalBlockPosProperty.java new file mode 100644 index 0000000..c9eb536 --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/OptionalBlockPosProperty.java @@ -0,0 +1,28 @@ +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 lol.pyr.znpcsplus.entity.EntityPropertyImpl; +import lol.pyr.znpcsplus.entity.PacketEntity; +import lol.pyr.znpcsplus.util.Vector3i; +import org.bukkit.entity.Player; + +import java.util.Map; +import java.util.Optional; + +public class OptionalBlockPosProperty extends EntityPropertyImpl { + private final int index; + + public OptionalBlockPosProperty(String name, Vector3i defaultValue, int index) { + super(name, defaultValue, Vector3i.class); + this.index = index; + } + + @Override + public void apply(Player player, PacketEntity entity, boolean isSpawned, Map properties) { + Vector3i value = entity.getProperty(this); + if (value == null) properties.put(index, new EntityData(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION, Optional.empty())); + else properties.put(index, new EntityData(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION, + Optional.of(new com.github.retrooper.packetevents.util.Vector3i(value.getX(), value.getY(), value.getZ())))); + } +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/serializers/GenericSerializer.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/serializers/GenericSerializer.java new file mode 100644 index 0000000..3ad3a5d --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/serializers/GenericSerializer.java @@ -0,0 +1,32 @@ +package lol.pyr.znpcsplus.entity.serializers; + +import lol.pyr.znpcsplus.entity.PropertySerializer; + +import java.util.function.Function; + +public class GenericSerializer implements PropertySerializer { + private final Function encoder; + private final Function decoder; + private final Class typeClass; + + public GenericSerializer(Function encoder, Function decoder, Class typeClass) { + this.encoder = encoder; + this.decoder = decoder; + this.typeClass = typeClass; + } + + @Override + public String serialize(T property) { + return encoder.apply(property); + } + + @Override + public T deserialize(String property) { + return decoder.apply(property); + } + + @Override + public Class getTypeClass() { + return typeClass; + } +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java index 4a8f77b..5048100 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java @@ -73,6 +73,10 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry { .setHologramOffset(-0.275) .addProperties("creeper_state", "creeper_charged")); + register(builder(p, "end_crystal", EntityTypes.END_CRYSTAL) + .setHologramOffset(0.025) + .addProperties("beam_target", "show_base")); + register(builder(p, "ender_dragon", EntityTypes.ENDER_DRAGON) .setHologramOffset(6.0245)); @@ -206,8 +210,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry { .addEquipmentProperties()); register(builder(p, "evoker", EntityTypes.EVOKER) - .setHologramOffset(-0.025) - .addProperties("evoker_spell")); + .setHologramOffset(-0.025)); register(builder(p, "llama", EntityTypes.LLAMA) .setHologramOffset(-0.105) diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/parsers/Vector3iParser.java b/plugin/src/main/java/lol/pyr/znpcsplus/parsers/Vector3iParser.java new file mode 100644 index 0000000..286729d --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/parsers/Vector3iParser.java @@ -0,0 +1,30 @@ +package lol.pyr.znpcsplus.parsers; + +import lol.pyr.director.adventure.command.CommandContext; +import lol.pyr.director.adventure.parse.ParserType; +import lol.pyr.director.common.command.CommandExecutionException; +import lol.pyr.director.common.message.Message; +import lol.pyr.znpcsplus.util.Vector3i; + +import java.util.Deque; + +public class Vector3iParser extends ParserType { + public Vector3iParser(Message message) { + super(message); + } + + @Override + public Vector3i parse(Deque deque) throws CommandExecutionException { + if (deque.size() == 0) { + return null; + } + try { + return new Vector3i( + Integer.parseInt(deque.pop()), + Integer.parseInt(deque.pop()), + Integer.parseInt(deque.pop())); + } catch (NumberFormatException e) { + throw new CommandExecutionException(); + } + } +}