diff --git a/build.gradle.kts b/build.gradle.kts index 554cd24..76aad03 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { } // Version of PacketEventsSK -val projectVersion = "1.1.2" +val projectVersion = "1.1.3" // API Version val apiVersion = "26.1.2" // Minimum paper version that PacketEventsSK supports @@ -53,7 +53,7 @@ dependencies { compileOnly("com.github.ShaneBeee:SkBee:3.23.0") // SkriptRegistration (SKR) - implementation("com.github.ShaneBeee:SkriptRegistration:1.4.2") + implementation("com.github.ShaneBeee:SkriptRegistration:1.4.3") // EntityLib implementation("io.github.tofaa2:spigot:3.3.0-SNAPSHOT") diff --git a/src/main/java/dev/threeadd/packeteventssk/api/general/packet/PacketTypeRegistry.java b/src/main/java/dev/threeadd/packeteventssk/api/general/packet/PacketTypeRegistry.java index e586ebe..5473a8d 100644 --- a/src/main/java/dev/threeadd/packeteventssk/api/general/packet/PacketTypeRegistry.java +++ b/src/main/java/dev/threeadd/packeteventssk/api/general/packet/PacketTypeRegistry.java @@ -13,9 +13,9 @@ public class PacketTypeRegistry { - private static final Map SEND_PACKETS_BY_NAME = new ConcurrentHashMap<>(); - private static final Map RECEIVE_PACKETS_BY_NAME = new ConcurrentHashMap<>(); - private static final List PACKETS; + private static final Map CLIENT_BOUND_PACKETS = new ConcurrentHashMap<>(); + private static final Map SERVER_BOUND_PACKETS = new ConcurrentHashMap<>(); + private static final List ALL_PACKETS; static { BiConsumer, PacketTypeCommon[]> populateMap = (map, types) -> { @@ -24,35 +24,50 @@ public class PacketTypeRegistry { } }; - populateMap.accept(SEND_PACKETS_BY_NAME, PacketType.Play.Server.values()); - populateMap.accept(SEND_PACKETS_BY_NAME, PacketType.Configuration.Server.values()); - populateMap.accept(SEND_PACKETS_BY_NAME, PacketType.Login.Server.values()); - populateMap.accept(SEND_PACKETS_BY_NAME, PacketType.Handshaking.Server.values()); - populateMap.accept(SEND_PACKETS_BY_NAME, PacketType.Status.Server.values()); + populateMap.accept(CLIENT_BOUND_PACKETS, PacketType.Play.Server.values()); + populateMap.accept(CLIENT_BOUND_PACKETS, PacketType.Configuration.Server.values()); + populateMap.accept(CLIENT_BOUND_PACKETS, PacketType.Login.Server.values()); + populateMap.accept(CLIENT_BOUND_PACKETS, PacketType.Handshaking.Server.values()); + populateMap.accept(CLIENT_BOUND_PACKETS, PacketType.Status.Server.values()); - populateMap.accept(RECEIVE_PACKETS_BY_NAME, PacketType.Play.Client.values()); - populateMap.accept(RECEIVE_PACKETS_BY_NAME, PacketType.Configuration.Client.values()); - populateMap.accept(RECEIVE_PACKETS_BY_NAME, PacketType.Login.Client.values()); - populateMap.accept(RECEIVE_PACKETS_BY_NAME, PacketType.Handshaking.Client.values()); - populateMap.accept(RECEIVE_PACKETS_BY_NAME, PacketType.Status.Client.values()); + populateMap.accept(SERVER_BOUND_PACKETS, PacketType.Play.Client.values()); + populateMap.accept(SERVER_BOUND_PACKETS, PacketType.Configuration.Client.values()); + populateMap.accept(SERVER_BOUND_PACKETS, PacketType.Login.Client.values()); + populateMap.accept(SERVER_BOUND_PACKETS, PacketType.Handshaking.Client.values()); + populateMap.accept(SERVER_BOUND_PACKETS, PacketType.Status.Client.values()); List packets = new ArrayList<>(); - packets.addAll(SEND_PACKETS_BY_NAME.values()); - packets.addAll(RECEIVE_PACKETS_BY_NAME.values()); - PACKETS = List.copyOf(packets); + packets.addAll(CLIENT_BOUND_PACKETS.values()); + packets.addAll(SERVER_BOUND_PACKETS.values()); + ALL_PACKETS = List.copyOf(packets); } public static List getAllPackets() { - return PACKETS; + return ALL_PACKETS; } public static @Nullable PacketTypeCommon getPacket(String rawName, boolean isSend) { String key = rawName.replace(" ", "_").toUpperCase(Locale.ENGLISH); if (isSend) { - return SEND_PACKETS_BY_NAME.get(key); + return CLIENT_BOUND_PACKETS.get(key); } else { - return RECEIVE_PACKETS_BY_NAME.get(key); + return SERVER_BOUND_PACKETS.get(key); } } + + /** + * Use {@link PacketTypeRegistry#getPacket(String, boolean)} if you have the direction (more performant) + */ + public static @Nullable PacketTypeCommon getPacket(String rawName) { + String key = rawName.replace(" ", "_").toUpperCase(Locale.ENGLISH); + + PacketTypeCommon type = CLIENT_BOUND_PACKETS.get(key); + + if (type == null) { + type = SERVER_BOUND_PACKETS.get(key); + } + + return type; + } } \ No newline at end of file diff --git a/src/main/java/dev/threeadd/packeteventssk/api/simple/PlayerSkinManager.java b/src/main/java/dev/threeadd/packeteventssk/api/simple/PlayerSkinManager.java index 156e80e..08feba7 100644 --- a/src/main/java/dev/threeadd/packeteventssk/api/simple/PlayerSkinManager.java +++ b/src/main/java/dev/threeadd/packeteventssk/api/simple/PlayerSkinManager.java @@ -20,8 +20,6 @@ import org.bukkit.World; import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.*; @@ -31,7 +29,6 @@ public class PlayerSkinManager { private static final Map> skinMap = new HashMap<>(); private static final Map globalSkinMap = new HashMap<>(); - private static final Logger log = LoggerFactory.getLogger(PlayerSkinManager.class); public static void setGlobalSkin(Player target, Skin skin) { if (target == null || skin == null) return; @@ -87,8 +84,6 @@ public static void clearAllSkins(Player target) { private static void updateSkin(Player target, Collection viewers) { User targetUser = PacketEvents.getAPI().getPlayerManager().getUser(target); - log.error("target: {}, viewers: {}", target.getName(), viewers.stream().map(Player::getName).toList()); - // unregister player for all players with the old data WrapperPlayServerPlayerInfoRemove infoRemove = new WrapperPlayServerPlayerInfoRemove(targetUser.getUUID()); for (Player viewer : viewers) { diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/EntityElementRegistration.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/EntityElementRegistration.java index 100c1b5..4027dd6 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/EntityElementRegistration.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/EntityElementRegistration.java @@ -7,14 +7,16 @@ import dev.threeadd.packeteventssk.element.entity.effect.EffSpawnFakeEntity; import dev.threeadd.packeteventssk.element.entity.effect.EffTeleportFakeEntity; import dev.threeadd.packeteventssk.element.entity.expression.ExprFakeEntitiesAll; -import dev.threeadd.packeteventssk.element.entity.expression.ExprFakeEntityEventValue; import dev.threeadd.packeteventssk.element.entity.expression.ExprFakeEntityFromId; import dev.threeadd.packeteventssk.element.entity.expression.ExprFakeEntityFromUuid; import dev.threeadd.packeteventssk.element.entity.expression.prop.ExprFakeEntityField; +import dev.threeadd.packeteventssk.element.entity.expression.prop.ExprMetaField; import dev.threeadd.packeteventssk.element.entity.expression.prop.ExprVisibleFakeEntities; import dev.threeadd.packeteventssk.element.entity.expression.prop.living.ExprFakeLivingEntityAttribute; -import dev.threeadd.packeteventssk.element.entity.field.FakeEntityFieldRegistry; +import dev.threeadd.packeteventssk.element.entity.field.entity.FakeEntityFieldRegistry; +import dev.threeadd.packeteventssk.element.entity.field.meta.MetaFieldRegistry; import dev.threeadd.packeteventssk.element.entity.section.SecExprNewFakeEntity; +import dev.threeadd.packeteventssk.element.entity.section.SecExprNewMeta; public class EntityElementRegistration implements SkriptElementRegistration { @@ -28,6 +30,7 @@ public void load(Registration reg) { // property registry (registered before the expr/sec using it) FakeEntityFieldRegistry.INSTANCE.registerAll(); + MetaFieldRegistry.INSTANCE.registerAll(); // start effects EffKillFakeEntity.register(reg); @@ -40,16 +43,17 @@ public void load(Registration reg) { ExprFakeLivingEntityAttribute.register(reg); ExprFakeEntityField.register(reg); + ExprMetaField.register(reg); ExprVisibleFakeEntities.register(reg); ExprFakeEntitiesAll.register(reg); - ExprFakeEntityEventValue.register(reg); ExprFakeEntityFromId.register(reg); ExprFakeEntityFromUuid.register(reg); // end expressions // start sections SecExprNewFakeEntity.register(reg); + SecExprNewMeta.register(reg); // end sections // types diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/Types.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/Types.java index 78a1f70..90979e2 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/Types.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/Types.java @@ -19,8 +19,11 @@ public static void register(Registration reg) { .examples(""" command cloneMe: trigger: - create new fake player entity at player for all players: - set fake skin of the fake entity to player's skin + set {_player} to a new fake player entity: + viewers: players + location: location of player + skin: skin of player + username: "Hello_World!" """) .since("1.0.0") .parser(new Parser<>() { @@ -42,5 +45,38 @@ public String toVariableNameString(WrapperEntity entity) { .register(); Converters.registerConverter(WrapperEntity.class, EntityMeta.class, WrapperEntity::getEntityMeta); + + reg.newType(EntityMeta.class, "entitymeta") + .user("fake ?entit(y|ies) meta") + .name("General - Entity Meta") + .description("The entity meta of a minecraft entity (this can both represent a fake entity's meta or a real entity's meta, but is mostly used for fake entities since the only use for real entities is for packet intercepting).") + .examples(""" + command spawn: + trigger: + set {_zombie} to a new fake zombie entity: + viewers: all players + location: location of player + + set {_meta} to fake entity meta of {_zombie} + set meta glowing state of {_meta} to true + """) + .since("1.1.0") + .parser(new Parser<>() { + @Override + public boolean canParse(ParseContext context) { + return false; + } + + @Override + public String toString(EntityMeta meta, int flags) { + return "entity meta"; + } + + @Override + public String toVariableNameString(EntityMeta meta) { + return "entitymeta:" + meta.hashCode(); + } + }) + .register(); } } diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntitiesAll.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntitiesAll.java index e140363..3da313a 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntitiesAll.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntitiesAll.java @@ -17,14 +17,19 @@ public static void register(Registration reg) { .name("Fake Entity - All Fake Entities") .description("Used to get a list of all fake entities that are viewable by least a single player") .examples(""" - command spawn: - trigger: - create a new fake zombie entity at player for players: - set fake scale attribute of the fake entity to 2 + command spawn: + trigger: + set {_zombie} to a new fake player entity: + location: location of player + viewers: all players + skin: skin of player + username: "the rizzler" - command listfakes: - trigger: - send "All fake entities: %all fake entities%" + set fake entity scale attribute of {_zombie} to 5 + + command listfakes: + trigger: + send "All fake entities: %all fake entities%" """) .since("1.1.0") .register(); diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntityEventValue.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntityEventValue.java deleted file mode 100644 index e482fcc..0000000 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntityEventValue.java +++ /dev/null @@ -1,26 +0,0 @@ -package dev.threeadd.packeteventssk.element.entity.expression; - -import ch.njol.skript.expressions.base.EventValueExpression; -import com.github.shanebeee.skr.Registration; -import me.tofaa.entitylib.wrapper.WrapperEntity; - -public class ExprFakeEntityEventValue extends EventValueExpression { - - public static void register(Registration reg) { - reg.newEventExpression(ExprFakeEntityEventValue.class, WrapperEntity.class, "[the] fake entity") - .name("Fake Entity Event Value") - .description("The fake entity involved in the event. (Only available in fake entity related events)") - .examples(""" - command test: - trigger: - create a new fake zombie entity at player for players: - broadcast the fake entity - """) - .since("1.0.0", "1.1.0 (switched to EventValueExpression)") - .register(); - } - - public ExprFakeEntityEventValue() { - super(WrapperEntity.class); - } -} diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntityFromId.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntityFromId.java index 733e535..97331a3 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntityFromId.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntityFromId.java @@ -20,9 +20,13 @@ public static void register(Registration reg) { command spawnfakeplayer: trigger: set {_p} to player - create new fake player entity at player for all players: - set fake skin of the fake entity to {_p}'s skin - add the fake entity id of the fake entity to {-id::*} + set {_player} to a new fake player entity: + location: location of player + viewers: all players + username: "test" + skin: skin of player + + add the fake entity id of {_player} to {-id::*} command lookup : trigger: diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntityFromUuid.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntityFromUuid.java index 5611190..1abf81c 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntityFromUuid.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/ExprFakeEntityFromUuid.java @@ -22,9 +22,13 @@ public static void register(Registration reg) { command spawnfakeplayer: trigger: set {_p} to player - create new fake player entity at player for all players: - set fake skin of the fake entity to {_p}'s skin - add fake uuid of the fake entity to {-uuid::*} + set {_player} to a new fake player entity: + location: location of player + viewers: all players + username: "test" + skin: skin of player + + add the fake entity uuid of {_player} to {-uuid::*} command lookup : trigger: diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/ExprFakeEntityField.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/ExprFakeEntityField.java index 7c41459..8f1b7ac 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/ExprFakeEntityField.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/ExprFakeEntityField.java @@ -8,20 +8,18 @@ import ch.njol.util.Kleenean; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.shanebeee.skr.Registration; +import com.google.common.primitives.Primitives; import dev.threeadd.packeteventssk.api.util.field.FieldAccessor; import dev.threeadd.packeteventssk.api.util.field.FieldSchema; -import dev.threeadd.packeteventssk.element.entity.field.FakeEntityFieldRegistry; +import dev.threeadd.packeteventssk.element.entity.field.entity.FakeEntityFieldRegistry; import me.tofaa.entitylib.wrapper.WrapperEntity; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Locale; +import java.util.*; import java.util.function.BiConsumer; -import java.util.function.Function; +import java.util.stream.Collectors; public class ExprFakeEntityField extends PropertyExpression { @@ -30,138 +28,228 @@ public static void register(Registration reg) { description.append("Gets or sets a fake entity property field value from a fake entity instance by its name.\nNote that some entities inherit properties (for example all entities inherit \"entity\" fields\n\n"); description.append("### Available Fake Entity Fields by Category\n"); - Collection> schemas = FakeEntityFieldRegistry.INSTANCE.getAllSchemas(); + List> schemas = new ArrayList<>(FakeEntityFieldRegistry.INSTANCE.getAllSchemas()); + schemas.sort(Comparator.comparingInt(schema -> schema.accessors().size())); for (FieldSchema schema : schemas) { - String fieldLines = schema.getReadableFields(); + + Set myFields = schema.accessors().stream() + .map(FieldAccessor::name) + .collect(Collectors.toSet()); + + FieldSchema parentSchema = null; + int maxSubsetSize = -1; + + for (FieldSchema other : schemas) { + if (other == schema) continue; + Set otherFields = other.accessors().stream() + .map(FieldAccessor::name) + .collect(Collectors.toSet()); + + if (myFields.containsAll(otherFields) && myFields.size() > otherFields.size()) { + if (otherFields.size() > maxSubsetSize) { + maxSubsetSize = otherFields.size(); + parentSchema = other; + } + } + } + + Set parentFieldNames = parentSchema != null + ? parentSchema.accessors().stream().map(FieldAccessor::name).collect(Collectors.toSet()) + : Collections.emptySet(); + + StringBuilder fieldLines = new StringBuilder(); + for (FieldAccessor field : schema.accessors()) { + if (!parentFieldNames.contains(field.name())) { + fieldLines.append(" - `").append(field.name()); + if (field.aliases().length > 0) { + fieldLines.append(" (").append(String.join(", ", field.aliases())).append(")"); + } + + fieldLines.append("`\n"); + } + } + if (!fieldLines.isEmpty()) { - description.append("* **") - .append(schema.type().getName().getKey().toLowerCase(Locale.ENGLISH).replace("_", " ")) - .append("** fields:\n") - .append(fieldLines) - .append("\n"); + String typeName = schema.type().getName().getKey().toLowerCase(Locale.ENGLISH).replace("_", " "); + description.append("* **").append(typeName).append("** fields:\n"); + + if (parentSchema != null) { + String parentName = parentSchema.type().getName().getKey().toLowerCase(Locale.ENGLISH).replace("_", " "); + description.append(" - *(Inherits all fields from **").append(parentName).append("**)*\n"); + } + + description.append(fieldLines).append("\n"); } } - reg.newPropertyExpression(ExprFakeEntityField.class, Object.class, "[entity] (field|fake) <[a-zA-Z0-9_ ]+>", "fakeentity") + reg.newPropertyExpression(ExprFakeEntityField.class, Object.class, "[fake] fake entity [field] <[a-zA-Z0-9_ ]+>", "fakeentity") .name("Fake Entity Property Field") .description(description.toString()) - // TODO example + .examples(""" + on load: + set {-notchSkin} to skin of player named "notch" + + command test5: + trigger: + set {_player} to a new fake player entity: + name: "test" + skin: skin of player + location: location of player + viewers: players + + wait 1 second + set fake entity skin of {_player} to {-notchSkin} + """) .since("1.1.2") .register(); } + private FieldAccessor fieldAccessor; private String fieldName; @SuppressWarnings("unchecked") @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { - this.fieldName = parseResult.regexes.getFirst().group().trim(); + String fieldName = parseResult.regexes.getFirst().group().trim(); boolean isValidField = false; for (FieldSchema def : FakeEntityFieldRegistry.INSTANCE.getAllSchemas()) { - if (def.getAccessor(this.fieldName) != null) { + FieldAccessor candidate = def.getAccessor(fieldName); + if (candidate != null) { + this.fieldAccessor = candidate; isValidField = true; break; } } if (!isValidField) { - Skript.error("The fake entity field '" + this.fieldName + "' is not registered or does not exist. Consider checking your spelling."); + Skript.error("The fake entity field '" + fieldName + "' is not registered or does not exist. Consider checking your spelling."); return false; } + this.fieldName = fieldName; setExpr((Expression) exprs[0]); return true; } + private @Nullable FieldAccessor resolveAccessor(WrapperEntity entity) { + EntityType type = entity.getEntityType(); + if (type == null) return null; + FieldSchema schema = FakeEntityFieldRegistry.INSTANCE.getSchema(type); + if (schema == null) return null; + return schema.getAccessor(this.fieldName); + } + @Nullable @Override protected Object[] get(Event event, WrapperEntity[] source) { - if (this.fieldName == null) return null; + if (this.fieldAccessor == null) return null; - List results = new ArrayList<>(); + List elements = new ArrayList<>(); for (WrapperEntity entity : source) { if (entity == null) continue; - FieldAccessor targetField = FakeEntityFieldRegistry.INSTANCE.getAccessor(entity.getClass(), fieldName); - if (targetField == null || targetField.getter() == null) continue; + FieldAccessor accessor = resolveAccessor(entity); + if (accessor == null) continue; - Object value = ((Function) targetField.getter()).apply(entity); - if (value != null) { - if (value.getClass().isArray()) { - int len = Array.getLength(value); - for (int i = 0; i < len; i++) { - results.add(Array.get(value, i)); - } - } else { - results.add(value); + Object value = accessor.getter().apply(entity); + if (value == null) continue; + + if (value.getClass().isArray()) { + int len = Array.getLength(value); + int i = 0; + while (i < len) { + elements.add(Array.get(value, i++)); } + } else { + elements.add(value); } } - return results.isEmpty() ? null : results.toArray(); + + if (elements.isEmpty()) return null; + + Class returnType = Primitives.wrap(getReturnType()); + return elements.toArray((Object[]) Array.newInstance(returnType, 0)); } @Nullable @Override public Class[] acceptChange(Changer.ChangeMode mode) { - if (mode == Changer.ChangeMode.SET && this.fieldName != null) { - List> acceptedTypes = new ArrayList<>(); - - for (FieldSchema def : FakeEntityFieldRegistry.INSTANCE.getAllSchemas()) { - FieldAccessor field = def.getAccessor(this.fieldName); - if (field != null) { - Class expected = field.expectedType(); - if (!acceptedTypes.contains(expected)) { - acceptedTypes.add(expected); - } - } - } - - if (!acceptedTypes.isEmpty()) { - return acceptedTypes.toArray(new Class[0]); - } + if (this.fieldAccessor == null) return null; + if (this.fieldAccessor.setter() == null) { + Skript.error("Cannot set " + this.fieldAccessor.name() + " because it is a read-only field."); + return null; } - return null; + + if (mode != Changer.ChangeMode.SET) return null; + + Class expected = this.fieldAccessor.expectedType(); + Class typeToAccept = expected.isArray() ? expected.getComponentType() : expected; + + return new Class[]{Primitives.wrap(typeToAccept)}; } @SuppressWarnings("unchecked") @Override public void change(Event event, Object[] delta, Changer.ChangeMode mode) { - if (mode != Changer.ChangeMode.SET || delta == null || delta.length == 0 || this.fieldName == null) return; - - Object newValue = delta.length == 1 ? delta[0] : delta; + if (mode != Changer.ChangeMode.SET || delta == null || delta.length == 0) return; for (WrapperEntity entity : getExpr().getArray(event)) { if (entity == null) continue; - FieldAccessor targetField = FakeEntityFieldRegistry.INSTANCE.getAccessor(entity.getClass(), fieldName); - if (targetField == null || targetField.setter() == null) continue; + FieldAccessor accessor = resolveAccessor(entity); + if (accessor == null || accessor.setter() == null) continue; - Class expected = targetField.expectedType(); - boolean isCompatible = expected == Object.class || expected.isInstance(newValue); + Object newValue; + Class expected = accessor.expectedType(); + + if (expected.isArray()) { + Class componentType = expected.getComponentType(); + Object typedArray = Array.newInstance(componentType, delta.length); + for (int i = 0; i < delta.length; i++) { + Array.set(typedArray, i, delta[i]); + } + newValue = typedArray; + } else { + if (delta.length == 1) { + newValue = delta[0]; + } else { + newValue = delta; + } + } + boolean isCompatible = expected == Object.class || expected.isInstance(newValue); if (!isCompatible && expected.isArray() && newValue.getClass().isArray()) { isCompatible = true; } if (!isCompatible) { - Skript.warning("Cannot set the fake entity field '" + this.fieldName + "' to a value of type " + newValue.getClass().getSimpleName() + ". Expected type: " + expected.getSimpleName()); + Skript.warning("Cannot set the fake entity field '" + accessor.name() + "' to a value of type " + newValue.getClass().getSimpleName() + ". Expected type: " + expected.getSimpleName()); continue; } - ((BiConsumer) targetField.setter()).accept(entity, newValue); + ((BiConsumer) accessor.setter()).accept(entity, newValue); } } + @Override + public boolean isSingle() { + if (this.fieldAccessor == null) return true; + return getExpr().isSingle() && !this.fieldAccessor.expectedType().isArray(); + } + @Override public Class getReturnType() { - return Object.class; + if (this.fieldAccessor == null) return Object.class; + Class expected = this.fieldAccessor.expectedType(); + return expected.isArray() ? expected.getComponentType() : expected; } @Override public String toString(@Nullable Event event, boolean debug) { String entity = getExpr() != null ? getExpr().toString(event, debug) : "fake entity"; - return "fake entity field " + fieldName + " of " + entity; + String name = this.fieldAccessor != null ? this.fieldAccessor.name() : this.fieldName; + return "fake entity field " + name + " of " + entity; } } \ No newline at end of file diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/ExprMetaField.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/ExprMetaField.java new file mode 100644 index 0000000..8e20841 --- /dev/null +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/ExprMetaField.java @@ -0,0 +1,243 @@ +package dev.threeadd.packeteventssk.element.entity.expression.prop; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.Changer; +import ch.njol.skript.expressions.base.PropertyExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser; +import ch.njol.util.Kleenean; +import com.github.retrooper.packetevents.protocol.entity.type.EntityType; +import com.github.shanebeee.skr.Registration; +import com.google.common.primitives.Primitives; +import dev.threeadd.packeteventssk.api.util.field.FieldAccessor; +import dev.threeadd.packeteventssk.api.util.field.FieldSchema; +import dev.threeadd.packeteventssk.element.entity.field.meta.MetaFieldRegistry; +import me.tofaa.entitylib.meta.EntityMeta; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Array; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +public class ExprMetaField extends PropertyExpression { + + public static void register(Registration reg) { + StringBuilder description = new StringBuilder(); + description.append("Gets or sets a metadata property field value from an entity meta instance by its name.\nNote that some entities inherit properties (for example all entities inherit \"entity\" fields\n\n"); + description.append("### Available Meta Fields by Category\n"); + + List> schemas = new ArrayList<>(MetaFieldRegistry.INSTANCE.getAllSchemas()); + schemas.sort(Comparator.comparingInt(schema -> schema.accessors().size())); + for (FieldSchema schema : schemas) { + + Set myFields = schema.accessors().stream() + .map(FieldAccessor::name) + .collect(Collectors.toSet()); + + FieldSchema parentSchema = null; + int maxSubsetSize = -1; + + for (FieldSchema other : schemas) { + if (other == schema) continue; + Set otherFields = other.accessors().stream() + .map(FieldAccessor::name) + .collect(Collectors.toSet()); + + if (myFields.containsAll(otherFields) && myFields.size() > otherFields.size()) { + if (otherFields.size() > maxSubsetSize) { + maxSubsetSize = otherFields.size(); + parentSchema = other; + } + } + } + + Set parentFieldNames = parentSchema != null + ? parentSchema.accessors().stream().map(FieldAccessor::name).collect(Collectors.toSet()) + : Collections.emptySet(); + + StringBuilder fieldLines = new StringBuilder(); + for (FieldAccessor field : schema.accessors()) { + if (!parentFieldNames.contains(field.name())) { + fieldLines.append(" - `").append(field.name()); + if (field.aliases().length > 0) { + fieldLines.append(" (").append(String.join(", ", field.aliases())).append(")"); + } + + fieldLines.append("`\n"); + } + } + + if (!fieldLines.isEmpty()) { + String typeName = schema.type().getName().getKey().toLowerCase(Locale.ENGLISH).replace("_", " "); + description.append("* **").append(typeName).append("** fields:\n"); + + if (parentSchema != null) { + String parentName = parentSchema.type().getName().getKey().toLowerCase(Locale.ENGLISH).replace("_", " "); + description.append(" - *(Inherits all fields from **").append(parentName).append("**)*\n"); + } + + description.append(fieldLines).append("\n"); + } + } + + reg.newPropertyExpression(ExprMetaField.class, Object.class, "[fake] [entity] meta [field] <[a-zA-Z0-9_ ]+>", "entitymeta") + .name("Entity Meta Property Field") + .description(description.toString()) + .examples(""" + on clientbound entity metadata: + # note that {_meta} is a copy of the packet's meta + set {_meta} to packet meta of event-packet + set meta glowing state of {_meta} to true + + # so we set it again here + set packet meta of event-packet to {_meta} + """) + .since("1.1.2") + .register(); + } + + private FieldAccessor fieldAccessor; + private String fieldName; + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { + String fieldName = parseResult.regexes.getFirst().group().trim(); + + boolean isValidField = false; + for (FieldSchema def : MetaFieldRegistry.INSTANCE.getAllSchemas()) { + this.fieldAccessor = def.getAccessor(fieldName); + if (this.fieldAccessor != null) { + isValidField = true; + break; + } + } + + if (!isValidField) { + Skript.error("The meta field '" + fieldName + "' is not registered or does not exist. Consider checking your spelling."); + return false; + } + + this.fieldName = fieldName; + setExpr((Expression) exprs[0]); + return true; + } + + private @Nullable FieldAccessor resolveAccessor(EntityMeta meta) { + return MetaFieldRegistry.INSTANCE.getAccessor(meta.getClass(), this.fieldName); + } + + @Nullable + @Override + protected Object[] get(Event event, EntityMeta[] source) { + if (this.fieldAccessor == null) return null; + + List elements = new ArrayList<>(); + + for (EntityMeta entity : source) { + FieldAccessor accessor = resolveAccessor(entity); + if (accessor == null) continue; + + Object value = accessor.getter().apply(entity); + if (value == null) continue; + + if (value.getClass().isArray()) { + int len = Array.getLength(value); + int i = 0; + while (i < len) { + elements.add(Array.get(value, i++)); + } + } else { + elements.add(value); + } + } + + if (elements.isEmpty()) return null; + + Class returnType = Primitives.wrap(getReturnType()); + return elements.toArray((Object[]) Array.newInstance(returnType, 0)); + } + + @Nullable + @Override + public Class[] acceptChange(Changer.ChangeMode mode) { + if (this.fieldAccessor == null) return null; + if (this.fieldAccessor.setter() == null) { + Skript.error("Cannot set " + this.fieldAccessor.name() + " because it is a read-only field."); + return null; + } + + if (mode != Changer.ChangeMode.SET) return null; + + Class expected = this.fieldAccessor.expectedType(); + Class typeToAccept = expected.isArray() ? expected.getComponentType() : expected; + + return new Class[]{Primitives.wrap(typeToAccept)}; + } + + @SuppressWarnings("unchecked") + @Override + public void change(Event event, Object[] delta, Changer.ChangeMode mode) { + if (mode != Changer.ChangeMode.SET || delta == null || delta.length == 0) return; + + for (Object obj : getExpr().getArray(event)) { + if (!(obj instanceof EntityMeta meta)) continue; + + FieldAccessor accessor = resolveAccessor(meta); + if (accessor == null || accessor.setter() == null) continue; + + Object newValue; + Class expected = accessor.expectedType(); + + if (expected.isArray()) { + Class componentType = expected.getComponentType(); + Object typedArray = Array.newInstance(componentType, delta.length); + for (int i = 0; i < delta.length; i++) { + Array.set(typedArray, i, delta[i]); + } + newValue = typedArray; + } else { + if (delta.length == 1) { + newValue = delta[0]; + } else { + newValue = delta; + } + } + + boolean isCompatible = expected == Object.class || expected.isInstance(newValue); + + if (!isCompatible && expected.isArray() && newValue.getClass().isArray()) { + isCompatible = true; + } + + if (!isCompatible) { + Skript.warning("Cannot set the meta field '" + accessor.name() + "' to a value of type " + newValue.getClass().getSimpleName() + ". Expected type: " + expected.getSimpleName()); + continue; + } + + ((BiConsumer) accessor.setter()).accept(meta, newValue); + } + } + + @Override + public boolean isSingle() { + if (this.fieldAccessor == null) return true; + return getExpr().isSingle() && !this.fieldAccessor.expectedType().isArray(); + } + + @Override + public Class getReturnType() { + if (this.fieldAccessor == null) return Object.class; + Class expected = this.fieldAccessor.expectedType(); + return expected.isArray() ? expected.getComponentType() : expected; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + String meta = getExpr() != null ? getExpr().toString(event, debug) : "meta"; + String name = this.fieldAccessor != null ? this.fieldAccessor.name() : this.fieldName; + return "meta field " + name + " of " + meta; + } +} \ No newline at end of file diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/ExprVisibleFakeEntities.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/ExprVisibleFakeEntities.java index f059291..29d8042 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/ExprVisibleFakeEntities.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/ExprVisibleFakeEntities.java @@ -23,14 +23,14 @@ public static void register(Registration reg) { .name("Fake Entity - Visible Fake Entities") .description("Used to get all fake entities viewed by a player") .examples(""" - command test: + command test1: trigger: - set {_p} to player - create a new fake zombie entity at player for players: - set fake scale attribute of the fake entity to 2 + set {_e} to a new fake zombie entity: + location: location of player + viewers: players - if visible fake entities of {_p} contains the fake entity: - send "You can see the fake entity!" to {_p} + if visible fake entities of player contains {_e}: + send "You can see the fake entity!" """) .since("1.0.1") .register(); diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/living/ExprFakeLivingEntityAttribute.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/living/ExprFakeLivingEntityAttribute.java index 342e9af..b49fc56 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/living/ExprFakeLivingEntityAttribute.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/expression/prop/living/ExprFakeLivingEntityAttribute.java @@ -15,17 +15,25 @@ import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; + public class ExprFakeLivingEntityAttribute extends PropertyExpression { public static void register(Registration reg) { - reg.newPropertyExpression(ExprFakeLivingEntityAttribute.class, Number.class, "fake %attributetype% attribute [value]", "fakeentity") + reg.newPropertyExpression(ExprFakeLivingEntityAttribute.class, Number.class, "[fake] fake entity [field] %attributetype% attribute [value]", "fakeentity") .name("Fake Living Entity Property - Attribute") .description("The attribute of a fake living entity.") .examples(""" - command test: + command spawn: trigger: - spawn a new fake player entity at player for players and store it in {_e} - set {_e}'s fake scale attribute to 2 + set {_zombie} to a new fake player entity: + location: location of player + viewers: all players + skin: skin of player + username: "the rizzler" + + set fake entity scale attribute of {_zombie} to 5 """) .since("1.0.1") .register(); @@ -51,11 +59,13 @@ protected Number[] get(Event event, WrapperEntity[] source) { Attribute bukkitAttr = this.attributeExpr.getSingle(event); if (bukkitAttr == null) return new Number[0]; - Number[] values = new Number[source.length]; - for (int i = 0; i < source.length; i++) { - values[i] = getAttributeValue(source[i], bukkitAttr); + List values = new ArrayList<>(source.length); + for (WrapperEntity entity : source) { + Number value = getAttributeValue(entity, bukkitAttr); + if (value != null) values.add(value); } - return values; + + return values.toArray(Number[]::new); } private @Nullable Number getAttributeValue(WrapperEntity entity, Attribute bukkitAttr) { @@ -66,7 +76,7 @@ protected Number[] get(Event event, WrapperEntity[] source) { .filter(prop -> prop.getAttribute() == peAttr) .findFirst() .map(Property::getValue) - .orElse(null); + .orElse(peAttr.getDefaultValue()); } return null; } @@ -102,7 +112,7 @@ public void change(Event event, Object @Nullable [] delta, Changer.ChangeMode mo .filter(prop -> prop.getAttribute() == peAttr) .findFirst() .map(Property::getValue) - .orElse(0d); + .orElse(peAttr.getDefaultValue()); switch (mode) { case SET -> livingFake.getAttributes().setAttribute(peAttr, deltaValue); @@ -123,6 +133,6 @@ public Class getReturnType() { public String toString(@Nullable Event event, boolean debug) { String attribute = this.attributeExpr.toString(event, debug); String entity = getExpr().toString(event, debug); - return String.format("fake %s attribute of %s", attribute, entity); + return String.format("fake entity %s attribute of %s", attribute, entity); } } \ No newline at end of file diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/field/FakeBaseEntityFieldRegistrar.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/entity/FakeBaseEntityFieldRegistrar.java similarity index 98% rename from src/main/java/dev/threeadd/packeteventssk/element/entity/field/FakeBaseEntityFieldRegistrar.java rename to src/main/java/dev/threeadd/packeteventssk/element/entity/field/entity/FakeBaseEntityFieldRegistrar.java index 49a81c5..3155aa1 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/field/FakeBaseEntityFieldRegistrar.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/entity/FakeBaseEntityFieldRegistrar.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.entity.field; +package dev.threeadd.packeteventssk.element.entity.field.entity; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/field/FakeEntityFieldRegistry.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/entity/FakeEntityFieldRegistry.java similarity index 93% rename from src/main/java/dev/threeadd/packeteventssk/element/entity/field/FakeEntityFieldRegistry.java rename to src/main/java/dev/threeadd/packeteventssk/element/entity/field/entity/FakeEntityFieldRegistry.java index 6609889..144c54e 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/field/FakeEntityFieldRegistry.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/entity/FakeEntityFieldRegistry.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.entity.field; +package dev.threeadd.packeteventssk.element.entity.field.entity; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/field/FakePlayerEntityFieldRegistrar.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/entity/FakePlayerEntityFieldRegistrar.java similarity index 92% rename from src/main/java/dev/threeadd/packeteventssk/element/entity/field/FakePlayerEntityFieldRegistrar.java rename to src/main/java/dev/threeadd/packeteventssk/element/entity/field/entity/FakePlayerEntityFieldRegistrar.java index 3d5675b..f1304ae 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/field/FakePlayerEntityFieldRegistrar.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/entity/FakePlayerEntityFieldRegistrar.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.entity.field; +package dev.threeadd.packeteventssk.element.entity.field.entity; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; import com.github.retrooper.packetevents.protocol.player.UserProfile; @@ -23,7 +23,7 @@ public void register() { .constructor(context -> { FakeBaseEntityFieldRegistrar.CommonEntityData data = FakeBaseEntityFieldRegistrar.CommonEntityData.extract(context); - String name = context.getRequired("username", String.class); + String name = context.getRequired("player username", String.class); UserProfile profile = new UserProfile(data.uuid(), name); WrapperPlayer player = new WrapperPlayer(profile, data.entityId()); diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/BaseMetaFieldRegistrar.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/BaseMetaFieldRegistrar.java similarity index 99% rename from src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/BaseMetaFieldRegistrar.java rename to src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/BaseMetaFieldRegistrar.java index 7f5fd10..a15798b 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/BaseMetaFieldRegistrar.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/BaseMetaFieldRegistrar.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.general.field.meta; +package dev.threeadd.packeteventssk.element.entity.field.meta; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.Timespan; diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/DisplayMetaFieldRegistrar.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/DisplayMetaFieldRegistrar.java similarity index 94% rename from src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/DisplayMetaFieldRegistrar.java rename to src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/DisplayMetaFieldRegistrar.java index f38bfe3..7d459c0 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/DisplayMetaFieldRegistrar.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/DisplayMetaFieldRegistrar.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.general.field.meta; +package dev.threeadd.packeteventssk.element.entity.field.meta; import ch.njol.skript.aliases.ItemType; import ch.njol.skript.util.ColorRGB; @@ -80,7 +80,7 @@ public void register() { "display shadow radius") .optionalField(Number.class, AbstractDisplayMeta::getShadowStrength, - (meta, newNum) -> meta.setShadowRadius(newNum.floatValue()), + (meta, newNum) -> meta.setShadowStrength(newNum.floatValue()), "display shadow strength") .optionalField(Number.class, AbstractDisplayMeta::getWidth, @@ -176,8 +176,14 @@ else if (display.isAlignRight()) return TextDisplay.TextAlignment.CENTER; }, (w, newAlign) -> { switch (newAlign) { - case LEFT -> w.setAlignLeft(true); - case RIGHT -> w.setAlignRight(true); + case LEFT -> { + w.setAlignLeft(true); + w.setAlignRight(false); + } + case RIGHT -> { + w.setAlignRight(true); + w.setAlignLeft(false); + } case CENTER -> { w.setAlignLeft(false); w.setAlignRight(false); @@ -223,8 +229,14 @@ else if (display.isAlignRight()) TextDisplay.TextAlignment alignment = context.getOptional("display text alignment", TextDisplay.TextAlignment.class); if (alignment != null) { switch (alignment) { - case LEFT -> meta.setAlignLeft(true); - case RIGHT -> meta.setAlignRight(true); + case LEFT -> { + meta.setAlignLeft(true); + meta.setAlignRight(false); + } + case RIGHT -> { + meta.setAlignRight(true); + meta.setAlignLeft(false); + } case CENTER -> { meta.setAlignLeft(false); meta.setAlignRight(false); diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/InteractionMetaFieldRegistrar.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/InteractionMetaFieldRegistrar.java similarity index 97% rename from src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/InteractionMetaFieldRegistrar.java rename to src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/InteractionMetaFieldRegistrar.java index 4f5144a..1902c05 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/InteractionMetaFieldRegistrar.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/InteractionMetaFieldRegistrar.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.general.field.meta; +package dev.threeadd.packeteventssk.element.entity.field.meta; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; import dev.threeadd.packeteventssk.api.util.field.ConstructionContext; diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/ItemMetaFieldRegistrar.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/ItemMetaFieldRegistrar.java similarity index 96% rename from src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/ItemMetaFieldRegistrar.java rename to src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/ItemMetaFieldRegistrar.java index 0d33416..d92d79b 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/ItemMetaFieldRegistrar.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/ItemMetaFieldRegistrar.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.general.field.meta; +package dev.threeadd.packeteventssk.element.entity.field.meta; import ch.njol.skript.aliases.ItemType; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/LivingMetaFieldRegistrar.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/LivingMetaFieldRegistrar.java similarity index 92% rename from src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/LivingMetaFieldRegistrar.java rename to src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/LivingMetaFieldRegistrar.java index edcc9cd..d807b91 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/LivingMetaFieldRegistrar.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/LivingMetaFieldRegistrar.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.general.field.meta; +package dev.threeadd.packeteventssk.element.entity.field.meta; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; @@ -20,7 +20,7 @@ public void register() { MetaFieldRegistry.INSTANCE.builder(EntityTypes.LIVINGENTITY, LivingEntityMeta.class) .optionalField(Number.class, LivingEntityMeta::getHealth, - (meta, newNum) -> meta.setHealth(newNum.intValue()), + (meta, newNum) -> meta.setHealth(newNum.floatValue()), "health", "hp") .constructor(context -> { LivingEntityMeta meta = (LivingEntityMeta) BaseMetaFieldRegistrar.BASE_ENTITY_META_CONSTRUCTOR.apply((ConstructionContext) context); @@ -34,7 +34,7 @@ public void register() { static final BiConsumer, LivingEntityMeta> LIVING_ENTITY_CONSUMER = (context, meta) -> { Number health = context.getOptional("health", Number.class); if (health != null) { - meta.setHealth(health.intValue()); + meta.setHealth(health.floatValue()); } }; } diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/MetaFieldRegistry.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/MetaFieldRegistry.java similarity index 96% rename from src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/MetaFieldRegistry.java rename to src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/MetaFieldRegistry.java index ea1fa1b..b64b1e4 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/field/meta/MetaFieldRegistry.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/field/meta/MetaFieldRegistry.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.general.field.meta; +package dev.threeadd.packeteventssk.element.entity.field.meta; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; diff --git a/src/main/java/dev/threeadd/packeteventssk/element/entity/section/SecExprNewFakeEntity.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/section/SecExprNewFakeEntity.java index 5399392..66d7e04 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/entity/section/SecExprNewFakeEntity.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/section/SecExprNewFakeEntity.java @@ -16,7 +16,7 @@ import dev.threeadd.packeteventssk.api.util.field.ConstructionContext; import dev.threeadd.packeteventssk.api.util.field.FieldAccessor; import dev.threeadd.packeteventssk.api.util.field.FieldSchema; -import dev.threeadd.packeteventssk.element.entity.field.FakeEntityFieldRegistry; +import dev.threeadd.packeteventssk.element.entity.field.entity.FakeEntityFieldRegistry; import io.github.retrooper.packetevents.util.SpigotConversionUtil; import me.tofaa.entitylib.wrapper.WrapperEntity; import org.bukkit.event.Event; @@ -26,44 +26,102 @@ import org.skriptlang.skript.lang.entry.EntryValidator; import java.util.*; +import java.util.stream.Collectors; public class SecExprNewFakeEntity extends SectionExpression { - private static EntryValidator VALIDATOR; + private static final Map, EntryValidator> VALIDATORS = new HashMap<>(); public static void register(Registration reg) { - SimpleEntryValidator builder = SimpleEntryValidator.builder(); for (FieldSchema def : FakeEntityFieldRegistry.INSTANCE.getAllSchemas()) { + SimpleEntryValidator builder = SimpleEntryValidator.builder(); for (FieldAccessor field : def.accessors()) { builder.addOptionalEntry(field.name(), Object.class); - for (String alias : field.aliases()) { // register aliases + for (String alias : field.aliases()) { builder.addOptionalEntry(alias, Object.class); } } + VALIDATORS.put(def, builder.build()); } - VALIDATOR = builder.build(); StringBuilder description = new StringBuilder(); description.append("Create a new fake entity from an entity type.\n\n"); description.append("### Available Entities and their fields\n"); - Collection> schemas = FakeEntityFieldRegistry.INSTANCE.getAllSchemas(); + List> schemas = new ArrayList<>(FakeEntityFieldRegistry.INSTANCE.getAllSchemas()); + schemas.sort(Comparator.comparingInt(schema -> schema.accessors().size())); for (FieldSchema schema : schemas) { - String fieldLines = schema.getReadableFields(); + + Set myFields = schema.accessors().stream() + .map(FieldAccessor::name) + .collect(Collectors.toSet()); + + FieldSchema parentSchema = null; + int maxSubsetSize = -1; + + for (FieldSchema other : schemas) { + if (other == schema) continue; + Set otherFields = other.accessors().stream() + .map(FieldAccessor::name) + .collect(Collectors.toSet()); + + if (myFields.containsAll(otherFields) && myFields.size() > otherFields.size()) { + if (otherFields.size() > maxSubsetSize) { + maxSubsetSize = otherFields.size(); + parentSchema = other; + } + } + } + + Set parentFieldNames = parentSchema != null + ? parentSchema.accessors().stream().map(FieldAccessor::name).collect(Collectors.toSet()) + : Collections.emptySet(); + + StringBuilder fieldLines = new StringBuilder(); + for (FieldAccessor field : schema.accessors()) { + if (!parentFieldNames.contains(field.name())) { + fieldLines.append(" - `").append(field.name()); + if (field.aliases().length > 0) { + fieldLines.append(" (").append(String.join(", ", field.aliases())).append(")"); + } + + fieldLines.append("`\n"); + } + } + if (!fieldLines.isEmpty()) { - description.append("* **") - .append(schema.type().toString().toLowerCase(Locale.ENGLISH).replace("_", " ")) - .append("** fields:\n") - .append(fieldLines) - .append("\n"); + String typeName = schema.type().getName().getKey().toLowerCase(Locale.ENGLISH).replace("_", " "); + description.append("* **").append(typeName).append("** fields:\n"); + + if (parentSchema != null) { + String parentName = parentSchema.type().getName().getKey().toLowerCase(Locale.ENGLISH).replace("_", " "); + description.append(" - *(Inherits all fields from **").append(parentName).append("**)*\n"); + } + + description.append(fieldLines).append("\n"); } } reg.newSimpleExpression(SecExprNewFakeEntity.class, WrapperEntity.class, "[a] [new] fake %*entitydata% entity") .name("Fake Entity - Create Fake Entity") .description(description.toString()) + .examples(""" + on load: + set {-notchSkin} to skin of player named "notch" + + command test5: + trigger: + set {_player} to a new fake player entity: + name: "test" + skin: skin of player + location: location of player + viewers: players + + wait 1 second + set fake entity skin of {_player} to {-notchSkin} + """) .since("1.0.0", "1.1.2 (changed to SectionExpression)") .register(); } @@ -105,13 +163,19 @@ public boolean init(Expression[] expressions, boolean hasRequiredFields = this.schema.accessors().stream().anyMatch(field -> !field.isOptional()); if (sectionNode == null) { if (hasRequiredFields) { - Skript.error("You must provide a section with the required fields to create a " + this.type.getName() + " fake entity."); + Skript.error("You must provide a section with the required fields to create a " + bukkitType.toString().toLowerCase(Locale.ENGLISH).replace("_", " ") + " fake entity."); return false; } return true; } - EntryContainer container = VALIDATOR.validate(sectionNode); + EntryValidator validator = VALIDATORS.get(this.schema); + if (validator == null) { + Skript.error("No validator found for " + bukkitType.toString().toLowerCase(Locale.ENGLISH).replace("_", " ") + " entity."); + return false; + } + + EntryContainer container = validator.validate(sectionNode); if (container == null) { return false; } diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/section/SecExprNewMeta.java b/src/main/java/dev/threeadd/packeteventssk/element/entity/section/SecExprNewMeta.java similarity index 64% rename from src/main/java/dev/threeadd/packeteventssk/element/general/section/SecExprNewMeta.java rename to src/main/java/dev/threeadd/packeteventssk/element/entity/section/SecExprNewMeta.java index 988da68..98ba4e5 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/section/SecExprNewMeta.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/entity/section/SecExprNewMeta.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.general.section; +package dev.threeadd.packeteventssk.element.entity.section; import ch.njol.skript.Skript; import ch.njol.skript.bukkitutil.EntityUtils; @@ -16,7 +16,7 @@ import dev.threeadd.packeteventssk.api.util.field.ConstructionContext; import dev.threeadd.packeteventssk.api.util.field.FieldAccessor; import dev.threeadd.packeteventssk.api.util.field.FieldSchema; -import dev.threeadd.packeteventssk.element.general.field.meta.MetaFieldRegistry; +import dev.threeadd.packeteventssk.element.entity.field.meta.MetaFieldRegistry; import io.github.retrooper.packetevents.util.SpigotConversionUtil; import me.tofaa.entitylib.meta.EntityMeta; import org.bukkit.event.Event; @@ -26,15 +26,16 @@ import org.skriptlang.skript.lang.entry.EntryValidator; import java.util.*; +import java.util.stream.Collectors; public class SecExprNewMeta extends SectionExpression { - private static EntryValidator VALIDATOR; + private static final Map, EntryValidator> VALIDATORS = new HashMap<>(); public static void register(Registration reg) { - SimpleEntryValidator builder = SimpleEntryValidator.builder(); for (FieldSchema def : MetaFieldRegistry.INSTANCE.getAllSchemas()) { + SimpleEntryValidator builder = SimpleEntryValidator.builder(); for (FieldAccessor field : def.accessors()) { builder.addOptionalEntry(field.name(), Object.class); @@ -42,22 +43,64 @@ public static void register(Registration reg) { builder.addOptionalEntry(alias, Object.class); } } + VALIDATORS.put(def, builder.build()); } - VALIDATOR = builder.build(); StringBuilder description = new StringBuilder(); description.append("Create a new meta from an entity type.\n\n"); description.append("### Available Metas and their fields\n"); - Collection> schemas = MetaFieldRegistry.INSTANCE.getAllSchemas(); + List> schemas = new ArrayList<>(MetaFieldRegistry.INSTANCE.getAllSchemas()); + schemas.sort(Comparator.comparingInt(schema -> schema.accessors().size())); for (FieldSchema schema : schemas) { - String fieldLines = schema.getReadableFields(); + + Set myFields = schema.accessors().stream() + .map(FieldAccessor::name) + .collect(Collectors.toSet()); + + FieldSchema parentSchema = null; + int maxSubsetSize = -1; + + for (FieldSchema other : schemas) { + if (other == schema) continue; + Set otherFields = other.accessors().stream() + .map(FieldAccessor::name) + .collect(Collectors.toSet()); + + if (myFields.containsAll(otherFields) && myFields.size() > otherFields.size()) { + if (otherFields.size() > maxSubsetSize) { + maxSubsetSize = otherFields.size(); + parentSchema = other; + } + } + } + + Set parentFieldNames = parentSchema != null + ? parentSchema.accessors().stream().map(FieldAccessor::name).collect(Collectors.toSet()) + : Collections.emptySet(); + + StringBuilder fieldLines = new StringBuilder(); + for (FieldAccessor field : schema.accessors()) { + if (!parentFieldNames.contains(field.name())) { + fieldLines.append(" - `").append(field.name()); + if (field.aliases().length > 0) { + fieldLines.append(" (").append(String.join(", ", field.aliases())).append(")"); + } + + fieldLines.append("`\n"); + } + } + if (!fieldLines.isEmpty()) { - description.append("* **") - .append(schema.type().toString().toLowerCase(Locale.ENGLISH).replace("_", " ")) - .append("** fields:\n") - .append(fieldLines) - .append("\n"); + String typeName = schema.type().getName().getKey().toLowerCase(Locale.ENGLISH).replace("_", " "); + description.append("* **").append(typeName).append("** fields:\n"); + + if (parentSchema != null) { + String parentName = parentSchema.type().getName().getKey().toLowerCase(Locale.ENGLISH).replace("_", " "); + description.append(" - *(Inherits all fields from **").append(parentName).append("**)*\n"); + } + + description.append(fieldLines).append("\n"); } } @@ -65,6 +108,23 @@ public static void register(Registration reg) { .name("General - Create Meta") .description(description.toString()) .since("1.1.2") + .examples(""" + set {_meta} to text display meta data: + display content: " " + display text shadowed state: true + display billboard: center + display scale: vector(1.2,1.2,1.2) + display translation: vector(-0.03,0.65,0) + display background color: rgb(random integer between 0 and 255, random integer between 0 and 255, random integer between 0 and 255) + display view range: 1 + display transform interpolation duration: 2 ticks + display interpolation delay: 0 ticks + + set {_entity} to a new fake text display entity: + viewers: all players + location: location of player ~ vector(0,0.5,0) + meta: {_meta} + """) .register(); } @@ -110,13 +170,19 @@ public boolean init(Expression[] expressions, boolean hasRequiredFields = this.schema.accessors().stream().anyMatch(field -> !field.isOptional()); if (sectionNode == null) { if (hasRequiredFields) { - Skript.error("You must provide a section with the required fields to create a " + this.type.getName() + " meta."); + Skript.error("You must provide a section with the required fields to create a " + bukkitType.toString().toLowerCase(Locale.ENGLISH).replace("_", " ") + " meta."); return false; } return true; } - EntryContainer container = VALIDATOR.validate(sectionNode); + EntryValidator validator = VALIDATORS.get(this.schema); + if (validator == null) { + Skript.error("No validator found for " + bukkitType.toString().toLowerCase(Locale.ENGLISH).replace("_", " ") + " meta."); + return false; + } + + EntryContainer container = validator.validate(sectionNode); if (container == null) { return false; } diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/GeneralElementRegistration.java b/src/main/java/dev/threeadd/packeteventssk/element/general/GeneralElementRegistration.java index 94c508a..83da757 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/GeneralElementRegistration.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/general/GeneralElementRegistration.java @@ -2,15 +2,14 @@ import com.github.shanebeee.skr.Registration; import dev.threeadd.packeteventssk.api.util.registry.element.SkriptElementRegistration; +import dev.threeadd.packeteventssk.element.general.condition.CondPacketTypeIsBound; import dev.threeadd.packeteventssk.element.general.effect.EffCancelPacket; import dev.threeadd.packeteventssk.element.general.effect.EffFetchSkin; import dev.threeadd.packeteventssk.element.general.effect.EffSendOrReceivePacket; import dev.threeadd.packeteventssk.element.general.event.EvtPacketSendOrReceive; import dev.threeadd.packeteventssk.element.general.expression.ExprSkinFromValue; import dev.threeadd.packeteventssk.element.general.expression.prop.*; -import dev.threeadd.packeteventssk.element.general.field.meta.MetaFieldRegistry; -import dev.threeadd.packeteventssk.element.general.field.packet.PacketFieldRegistry; -import dev.threeadd.packeteventssk.element.general.section.SecExprNewMeta; +import dev.threeadd.packeteventssk.element.general.field.PacketFieldRegistry; import dev.threeadd.packeteventssk.element.general.section.SecExprNewPacket; public class GeneralElementRegistration implements SkriptElementRegistration { @@ -25,7 +24,10 @@ public void load(Registration reg) { // property registries (registered before the expr/sec using it) PacketFieldRegistry.INSTANCE.registerAll(); - MetaFieldRegistry.INSTANCE.registerAll(); + + // start conditions + CondPacketTypeIsBound.register(reg); + // end conditions // start effects EffCancelPacket.register(reg); @@ -39,7 +41,6 @@ public void load(Registration reg) { // start expressions ExprEntityId.register(reg); - ExprMetaField.register(reg); ExprPacketField.register(reg); ExprPacketPacketType.register(reg); ExprPlayerSkin.register(reg); @@ -48,7 +49,6 @@ public void load(Registration reg) { // end expressions // start sections - SecExprNewMeta.register(reg); SecExprNewPacket.register(reg); // end sections diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/Types.java b/src/main/java/dev/threeadd/packeteventssk/element/general/Types.java index b492c28..85212da 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/Types.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/general/Types.java @@ -1,10 +1,13 @@ package dev.threeadd.packeteventssk.element.general; import ch.njol.skript.classes.Parser; +import ch.njol.skript.classes.Serializer; import ch.njol.skript.lang.ParseContext; +import ch.njol.yggdrasil.Fields; import com.github.retrooper.packetevents.protocol.PacketSide; import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon; import com.github.retrooper.packetevents.protocol.player.InteractionHand; +import com.github.retrooper.packetevents.protocol.player.TextureProperty; import com.github.retrooper.packetevents.protocol.world.blockentity.BlockEntityType; import com.github.retrooper.packetevents.protocol.world.blockentity.BlockEntityTypes; import com.github.retrooper.packetevents.wrapper.PacketWrapper; @@ -13,14 +16,19 @@ import dev.threeadd.packeteventssk.api.entity.Skin; import dev.threeadd.packeteventssk.api.general.packet.PacketTypeRegistry; import dev.threeadd.packeteventssk.api.util.DebugUtil; -import me.tofaa.entitylib.meta.EntityMeta; import org.bukkit.block.sign.Side; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.converter.Converters; +import java.io.StreamCorruptedException; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; +import java.util.stream.Collectors; public class Types { + @SuppressWarnings("UnstableApiUsage") public static void register(Registration reg) { reg.newType(PacketWrapper.class, "packet") .user("packet") @@ -45,16 +53,22 @@ public String toString(PacketWrapper packet, int flags) { @Override public String toVariableNameString(PacketWrapper packet) { - return "packet:" + packet.hashCode(); + return "packet:" + packet.hashCode(); // TODO improve } }) .register(); + Converters.registerConverter(PacketWrapper.class, PacketTypeCommon.class, packet -> packet.getPacketTypeData().getPacketType()); + reg.newType(PacketTypeCommon.class, "packettype") .user("packet ?types?") .name("General - Packet Type") .description("Represents a specific type of packet (e.g. clientbound chunk data packet)") - // TODO example + .examples(""" + on any packet: + if event-packet is clientbound: + send packet type of event-packet to console + """) .since("1.0.0") .supplier(() -> PacketTypeRegistry.getAllPackets().iterator()) .parser(new Parser<>() { @@ -92,45 +106,23 @@ public String toString(PacketTypeCommon type, int flags) { @Override public String toVariableNameString(PacketTypeCommon type) { - return "packettype:" + type.getName(); - } - }) - .register(); - - reg.newType(EntityMeta.class, "entitymeta") - .user("fake ?entit(y|ies) meta") - .name("General - Entity Meta") - .description("The entity meta of a minecraft entity (this can both represent a fake entity's meta or a real entity's meta, but is mostly used for fake entities since the only use for real entities is for packet intercepting).") - .examples(""" - command spawn: - trigger: - create a new fake zombie entity at player for players: - set fake scale attribute of the fake entity to 2 - """) - .since("1.1.0") - .parser(new Parser<>() { - @Override - public boolean canParse(ParseContext context) { - return false; - } - - @Override - public String toString(EntityMeta meta, int flags) { - return "entity meta"; - } - - @Override - public String toVariableNameString(EntityMeta meta) { - return "entitymeta:" + meta.hashCode(); + return "packettype:" + type.getName().toLowerCase(Locale.ENGLISH).replace("_", " "); } }) + // TODO add serialization when skript fixes yggdrasil .register(); reg.newType(BlockEntityType.class, "blockentitytype") .user("block ?entit(y|ies) types?") .name("General - Block Entity Type") .description("Represents a type of block entity (e.g. chest, sign, etc.)") - // TODO example + .examples(""" + # Snippet from https://github.com/3add/PacketEventsSK/wiki/Examples#sign-exploit + set {_setTextPacket} to a new clientbound block entity data packet: + block position: {_pos} + block entity type: sign block entity type + nbt compound: createSignNBT({_keybind}) + """) .since("1.1.0") .supplier(() -> BlockEntityTypes.values().iterator()) .parser(new Parser<>() { @@ -151,7 +143,37 @@ public String toString(BlockEntityType type, int flags) { @Override public String toVariableNameString(BlockEntityType type) { - return "blockentitytype:" + type.getName().getKey(); + return "blockentitytype:" + type.getName().getKey().toLowerCase(Locale.ENGLISH).replace("_", " "); + } + }) + .serializer(new Serializer<>() { + + @Override + public Fields serialize(BlockEntityType o) { + Fields fields = new Fields(); + fields.putObject("name", o.getName().getKey().toLowerCase(Locale.ENGLISH)); + + return fields; + } + + @Override + public BlockEntityType deserialize(Fields fields) throws StreamCorruptedException { + String name = fields.getObject("name", String.class); + if (name == null) { + throw new StreamCorruptedException("Missing block entity type name"); + } + + return BlockEntityTypes.getByName(name); + } + + @Override + public boolean mustSyncDeserialization() { + return true; + } + + @Override + protected boolean canBeInstantiated() { + return false; } }) .register(); @@ -161,7 +183,13 @@ public String toVariableNameString(BlockEntityType type) { .user("sign ?sides?") .name("General - Sign Side") .description("Represents a side of a sign block (front or back)") - // TODO example + .examples(""" + # Snippet from https://github.com/3add/PacketEventsSK/wiki/Examples#sign-exploit + set {_setTextPacket} to a new clientbound block entity data packet: + block position: {_pos} + block entity type: sign block entity type + nbt compound: createSignNBT({_keybind}) + """) .since("1.1.0") .register(); @@ -170,7 +198,22 @@ public String toVariableNameString(BlockEntityType type) { .user("interaction ?hands?") .name("General - Interaction Hand") .description("Represents an interaction hand (main hand or off hand)") - // TODO example + .examples(""" + # Snippet from https://github.com/3add/PacketEventsSK/wiki/Examples#welcome + on serverbound interact entity: + set {_id} to packet field entity id of event-packet + set {_hand} to packet field hand of event-packet + set {_sneaking} to packet field sneaking state of event-packet + + if all: + {_id} is {-interactables::%player's uuid%} + # this packet is sent for each hand when just regular clicking + # "main hand" is parsed as equipment slot if literal so we parse from text + {_hand} is "main hand" parsed as interaction hand + {_sneaking} is false + then: + send "welcome player!" + """) .since("1.1.2") .register(); @@ -194,12 +237,59 @@ public boolean canParse(ParseContext context) { @Override public String toString(Skin skin, int flags) { - return "skin: " + skin.properties().stream().map(prop -> "value: '" + prop.getValue() + "', signature: '" + prop.getSignature() + "'").toList(); + return skin.properties().stream() + .map(prop -> "value: '" + prop.getValue() + "', signature: '" + prop.getSignature() + "'") + .collect(Collectors.joining(", ")); } @Override public String toVariableNameString(Skin skin) { - return "skin:" + skin.hashCode(); + return "skin:" + toString(skin, 0); + } + }) + .serializer(new Serializer<>() { + + @Override + public Fields serialize(Skin o) { + Fields fields = new Fields(); + + String[] values = o.properties().stream().map(TextureProperty::getValue).toArray(String[]::new); + String[] signatures = o.properties().stream().map(TextureProperty::getSignature).toArray(String[]::new); + + fields.putObject("values", values); + fields.putObject("signatures", signatures); + + return fields; + } + + @Override + protected Skin deserialize(Fields fields) throws StreamCorruptedException { + String[] values = fields.getObject("values", String[].class); + String[] signatures = fields.getObject("signatures", String[].class); + + if (values == null || values.length == 0) { + throw new StreamCorruptedException("Skin properties cannot be empty"); + } + + List properties = new ArrayList<>(); + for (int i = 0; i < values.length; i++) { + String value = values[i]; + String signature = (signatures != null && i < signatures.length) ? signatures[i] : null; + + properties.add(new TextureProperty("textures", value, signature)); + } + + return new Skin(properties); + } + + @Override + public boolean mustSyncDeserialization() { + return true; + } + + @Override + protected boolean canBeInstantiated() { + return false; } }) .register(); diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/condition/CondPacketTypeIsBound.java b/src/main/java/dev/threeadd/packeteventssk/element/general/condition/CondPacketTypeIsBound.java new file mode 100644 index 0000000..1b3b273 --- /dev/null +++ b/src/main/java/dev/threeadd/packeteventssk/element/general/condition/CondPacketTypeIsBound.java @@ -0,0 +1,51 @@ +package dev.threeadd.packeteventssk.element.general.condition; + +import ch.njol.skript.conditions.base.PropertyCondition; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser; +import ch.njol.util.Kleenean; +import com.github.retrooper.packetevents.protocol.PacketSide; +import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon; +import com.github.shanebeee.skr.Registration; + +public class CondPacketTypeIsBound extends PropertyCondition { + + public static void register(Registration reg) { + reg.newPropertyCondition(CondPacketTypeIsBound.class, PropertyType.BE, "(:(client|server))[ ]bound", "packettypes") + .name("General - Packet Type Bound") + .description("See in which direction a packet (or packettype) is bound") + .examples(""" + on any packet: + if event-packet is clientbound: + send packet type of event-packet to console + """) + .since("1.1.3") + .register(); + } + + private boolean isClientbound = false; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { + + if (!super.init(expressions, matchedPattern, isDelayed, parseResult)) { + return false; + } + + isClientbound = parseResult.hasTag("client"); + + return true; + } + + @Override + public boolean check(PacketTypeCommon value) { + // bad naming from packetevents + if (isClientbound) return value.getSide() == PacketSide.SERVER; + else return value.getSide() == PacketSide.CLIENT; + } + + @Override + protected String getPropertyName() { + return isClientbound ? "clientbound" : "serverbound"; + } +} diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/expression/prop/ExprMetaField.java b/src/main/java/dev/threeadd/packeteventssk/element/general/expression/prop/ExprMetaField.java deleted file mode 100644 index b036e78..0000000 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/expression/prop/ExprMetaField.java +++ /dev/null @@ -1,175 +0,0 @@ -package dev.threeadd.packeteventssk.element.general.expression.prop; - -import ch.njol.skript.Skript; -import ch.njol.skript.classes.Changer; -import ch.njol.skript.expressions.base.PropertyExpression; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.SkriptParser; -import ch.njol.util.Kleenean; -import com.github.retrooper.packetevents.protocol.entity.type.EntityType; -import com.github.shanebeee.skr.Registration; -import dev.threeadd.packeteventssk.api.util.field.FieldAccessor; -import dev.threeadd.packeteventssk.api.util.field.FieldSchema; -import dev.threeadd.packeteventssk.element.general.field.meta.MetaFieldRegistry; -import me.tofaa.entitylib.meta.EntityMeta; -import org.bukkit.event.Event; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Locale; -import java.util.function.BiConsumer; -import java.util.function.Function; - -public class ExprMetaField extends PropertyExpression { - - public static void register(Registration reg) { - StringBuilder description = new StringBuilder(); - description.append("Gets or sets a metadata property field value from an entity meta instance by its name.\nNote that some entities inherit properties (for example all entities inherit \"entity\" fields\n\n"); - description.append("### Available Meta Fields by Category\n"); - - Collection> schemas = MetaFieldRegistry.INSTANCE.getAllSchemas(); - for (FieldSchema schema : schemas) { - String fieldLines = schema.getReadableFields(); - if (!fieldLines.isEmpty()) { - description.append("* **") - .append(schema.type().getName().getKey().toLowerCase(Locale.ENGLISH).replace("_", " ")) - .append("** fields:\n") - .append(fieldLines) - .append("\n"); - } - } - - reg.newPropertyExpression(ExprMetaField.class, Object.class, "[meta] (field|fake) <[a-zA-Z0-9_ ]+>", "entitymeta") - .name("Entity Meta Property Field") - .description(description.toString()) - .examples(""" - on clientbound entity metadata: - # note that {_meta} is a copy of the packet's meta - set {_meta} to meta of event-packet - set glowing state of {_meta} to true - - # so we set it again here - set meta of event-packet to {_meta} - """) - .since("1.1.2") - .register(); - } - - private String fieldName; - - @SuppressWarnings("unchecked") - @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { - this.fieldName = parseResult.regexes.getFirst().group().trim(); - - boolean isValidField = false; - for (FieldSchema def : MetaFieldRegistry.INSTANCE.getAllSchemas()) { - if (def.getAccessor(this.fieldName) != null) { - isValidField = true; - break; - } - } - - if (!isValidField) { - Skript.error("The meta field '" + this.fieldName + "' is not registered or does not exist. Consider checking your spelling."); - return false; - } - - setExpr((Expression) exprs[0]); - return true; - } - - @Nullable - @Override - protected Object[] get(Event event, EntityMeta[] source) { - if (this.fieldName == null) return null; - - List results = new ArrayList<>(); - - for (EntityMeta meta : source) { - if (meta == null) continue; - - FieldAccessor targetField = MetaFieldRegistry.INSTANCE.getAccessor(meta.getClass(), fieldName); - if (targetField == null || targetField.getter() == null) continue; - - Object value = ((Function) targetField.getter()).apply(meta); - if (value != null) { - if (value.getClass().isArray()) { - int len = Array.getLength(value); - for (int i = 0; i < len; i++) { - results.add(Array.get(value, i)); - } - } else { - results.add(value); - } - } - } - return results.isEmpty() ? null : results.toArray(); - } - - @Nullable - @Override - public Class[] acceptChange(Changer.ChangeMode mode) { - if (mode == Changer.ChangeMode.SET && this.fieldName != null) { - List> acceptedTypes = new ArrayList<>(); - - for (FieldSchema def : MetaFieldRegistry.INSTANCE.getAllSchemas()) { - FieldAccessor field = def.getAccessor(this.fieldName); - if (field != null) { - Class expected = field.expectedType(); - if (!acceptedTypes.contains(expected)) { - acceptedTypes.add(expected); - } - } - } - - if (!acceptedTypes.isEmpty()) { - return acceptedTypes.toArray(new Class[0]); - } - } - return null; - } - - @SuppressWarnings("unchecked") - @Override - public void change(Event event, Object[] delta, Changer.ChangeMode mode) { - if (mode != Changer.ChangeMode.SET || delta == null || delta.length == 0 || this.fieldName == null) return; - - Object newValue = delta.length == 1 ? delta[0] : delta; - - for (EntityMeta meta : getExpr().getArray(event)) { - if (meta == null) continue; - - FieldAccessor targetField = MetaFieldRegistry.INSTANCE.getAccessor(meta.getClass(), fieldName); - if (targetField == null || targetField.setter() == null) continue; - - Class expected = targetField.expectedType(); - boolean isCompatible = expected == Object.class || expected.isInstance(newValue); - - if (!isCompatible && expected.isArray() && newValue.getClass().isArray()) { - isCompatible = true; - } - - if (!isCompatible) { - Skript.warning("Cannot set the meta field '" + this.fieldName + "' to a value of type " + newValue.getClass().getSimpleName() + ". Expected type: " + expected.getSimpleName()); - continue; - } - - ((BiConsumer) targetField.setter()).accept(meta, newValue); - } - } - - @Override - public Class getReturnType() { - return Object.class; - } - - @Override - public String toString(@Nullable Event event, boolean debug) { - String meta = getExpr() != null ? getExpr().toString(event, debug) : "meta"; - return "meta field " + fieldName + " of " + meta; - } -} \ No newline at end of file diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/expression/prop/ExprPacketField.java b/src/main/java/dev/threeadd/packeteventssk/element/general/expression/prop/ExprPacketField.java index fd1a2fa..ca469fd 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/expression/prop/ExprPacketField.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/general/expression/prop/ExprPacketField.java @@ -10,15 +10,17 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon; import com.github.retrooper.packetevents.wrapper.PacketWrapper; import com.github.shanebeee.skr.Registration; +import com.google.common.primitives.Primitives; import dev.threeadd.packeteventssk.api.general.packet.PacketSendOrReceiveEvent; import dev.threeadd.packeteventssk.api.util.field.FieldAccessor; import dev.threeadd.packeteventssk.api.util.field.FieldSchema; import dev.threeadd.packeteventssk.element.general.event.EvtPacketSendOrReceive; import dev.threeadd.packeteventssk.element.general.event.EvtPacketSendOrReceive.PacketSendOrReceiveParserData; -import dev.threeadd.packeteventssk.element.general.field.packet.PacketFieldRegistry; +import dev.threeadd.packeteventssk.element.general.field.PacketFieldRegistry; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -29,7 +31,6 @@ public class ExprPacketField extends PropertyExpression { public static void register(Registration reg) { - StringBuilder description = new StringBuilder(); description.append("Gets or sets a field's value from a packet by its name.\n\n"); description.append("### Available Packets and their fields\n"); @@ -39,64 +40,75 @@ public static void register(Registration reg) { String fieldLines = schema.getReadableFields(); if (!fieldLines.isEmpty()) { description.append("* **") - .append(schema.type().toString().toLowerCase(Locale.ENGLISH).replace("_", " ")) + .append(schema.type().getName().toLowerCase(Locale.ENGLISH).replace("_", " ")) .append("** fields:\n") .append(fieldLines) .append("\n"); } } - reg.newPropertyExpression(ExprPacketField.class, Object.class, "[packet] (field|fake) <[a-zA-Z0-9_ ]+>", "packet") + reg.newPropertyExpression(ExprPacketField.class, Object.class, "[fake] packet [field] <[a-zA-Z0-9_ ]+>", "packet") .name("General - Packet Field") .description(description.toString()) .examples(""" - on clientbound entity metadata netty processed: - set {_meta} to entity meta of event-packet - set fake glowing state of {_meta} to true - set entity meta of event-packet to {_meta} + on clientbound entity metadata: + # note that {_meta} is a copy of the packet's meta + set {_meta} to packet meta of event-packet + set meta glowing state of {_meta} to true + + # so we set it again here + set packet meta of event-packet to {_meta} """) .since("1.1.0", "1.1.1 (fixed bugs)") .register(); } - private String fieldName; + private FieldAccessor, ?> fieldAccessor; // event-specific or type-hint for global + private String fieldName; // only used in global mode + private boolean globalMode = false; @SuppressWarnings("unchecked") @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { - this.fieldName = parseResult.regexes.getFirst().group().trim(); - + String fieldName = parseResult.regexes.getFirst().group().trim(); PacketSendOrReceiveParserData data = getParser().getData(PacketSendOrReceiveParserData.class); if (getParser().isCurrentEvent(PacketSendOrReceiveEvent.class)) { // for the listening event - PacketTypeCommon eventPacketType = data.getPacketType(); - if (eventPacketType == null) return false; // shouldn't ever happen + if (eventPacketType == null) return false; - FieldSchema> def = PacketFieldRegistry.INSTANCE.getSchema(eventPacketType); - - if (def == null) { + FieldSchema> schema = PacketFieldRegistry.INSTANCE.getSchema(eventPacketType); + if (schema == null) { Skript.error("No fields are currently registered for the " + eventPacketType.getName().toLowerCase(Locale.ENGLISH).replace("_", " ") + " packet."); return false; } - if (def.getAccessor(this.fieldName) == null) { - Skript.error("The field '" + this.fieldName + "' does not exist in a " + eventPacketType.getName().toLowerCase(Locale.ENGLISH).replace("_", " ") + " packet."); + this.fieldAccessor = schema.getAccessor(fieldName); + if (this.fieldAccessor == null) { + Skript.error("The field '" + fieldName + "' does not exist in a " + eventPacketType.getName().toLowerCase(Locale.ENGLISH).replace("_", " ") + " packet."); return false; } + + this.globalMode = false; + } else { // more global check boolean isValidField = false; for (FieldSchema> def : PacketFieldRegistry.INSTANCE.getAllSchemas()) { - if (def.getAccessor(this.fieldName) != null) { + FieldAccessor, ?> candidate = def.getAccessor(fieldName); + if (candidate != null) { + this.fieldAccessor = candidate; // kept only as a type hint isValidField = true; break; } } if (!isValidField) { - Skript.error("The packet field '" + this.fieldName + "' is not registered or does not exist. Consider checking your spelling."); + Skript.error("The packet field '" + fieldName + "' is not registered or does not exist. Consider checking your spelling."); return false; } + + this.fieldName = fieldName; + this.globalMode = true; } setExpr((Expression) exprs[0]); @@ -104,125 +116,129 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye } @SuppressWarnings("UnstableApiUsage") + private @Nullable FieldAccessor, ?> resolveAccessor(PacketWrapper wrapper) { + if (!globalMode) return this.fieldAccessor; + PacketTypeCommon type = wrapper.getPacketTypeData().getPacketType(); + if (type == null) return null; + FieldSchema> schema = PacketFieldRegistry.INSTANCE.getSchema(type); + if (schema == null) return null; + return schema.getAccessor(this.fieldName); + } + @Override protected Object[] get(Event event, PacketWrapper[] source) { - if (this.fieldName == null) return null; - - List results = new ArrayList<>(); + List elements = new ArrayList<>(); for (PacketWrapper wrapper : source) { if (wrapper == null) continue; - PacketTypeCommon type = wrapper.getPacketTypeData().getPacketType(); - FieldSchema> definition = PacketFieldRegistry.INSTANCE.getSchema(type); - - if (definition == null) continue; + FieldAccessor, ?> accessor = resolveAccessor(wrapper); + if (accessor == null) continue; - FieldAccessor, ?> targetField = definition.getAccessor(this.fieldName); + Object value = accessor.getter().apply(wrapper); + if (value == null) continue; - if (targetField == null || targetField.getter() == null) continue; - - Object value = targetField.getter().apply(wrapper); - if (value != null) { - results.add(value); + if (value.getClass().isArray()) { + int len = Array.getLength(value); + int i = 0; + while (i < len) { + elements.add(Array.get(value, i++)); + } + } else { + elements.add(value); } } - return results.isEmpty() ? null : results.toArray(); + if (elements.isEmpty()) return null; + + Class returnType = Primitives.wrap(getReturnType()); // wrap primitives to avoid java.lang.ClassCastException on arrays of primitives + return elements.toArray((Object[]) Array.newInstance(returnType, 0)); } @Override public Class[] acceptChange(Changer.ChangeMode mode) { - if (mode == Changer.ChangeMode.SET && this.fieldName != null) { - PacketSendOrReceiveParserData data = getParser().getData(PacketSendOrReceiveParserData.class); - - if (getParser().isCurrentEvent(PacketSendOrReceiveEvent.class)) { - if (data.getProcessType() != EvtPacketSendOrReceive.ProcessType.NETTY) { - Skript.error("You can't alter packets in a " + (data.getProcessType() == null ? "unknown" : data.getProcessType().toString().toLowerCase(Locale.ENGLISH)) + " processed event, the packets have already been processed at that point. Use a netty processed event instead."); - return null; - } else if (data.getPriority() == PacketListenerPriority.MONITOR) { - Skript.error("You can't alter packets when using the \"monitor\" listening priority."); - return null; - } + if (this.fieldAccessor == null || this.fieldAccessor.setter() == null) { + String name = globalMode ? fieldName : (this.fieldAccessor != null ? this.fieldAccessor.name() : "unknown"); + Skript.error("Cannot set " + name + " because it is a read-only field."); + return null; + } - PacketTypeCommon eventPacketType = data.getPacketType(); - if (eventPacketType != null) { - FieldSchema> def = PacketFieldRegistry.INSTANCE.getSchema(eventPacketType); - if (def != null) { - FieldAccessor, ?> field = def.getAccessor(this.fieldName); - if (field != null) { - return new Class[]{field.expectedType()}; - } - } - } - } + if (mode != Changer.ChangeMode.SET) return null; - List> acceptedTypes = new ArrayList<>(); - for (FieldSchema> def : PacketFieldRegistry.INSTANCE.getAllSchemas()) { - FieldAccessor, ?> field = def.getAccessor(this.fieldName); - if (field != null) { - Class expected = field.expectedType(); - if (!acceptedTypes.contains(expected)) { - acceptedTypes.add(expected); - } - } - } + PacketSendOrReceiveParserData data = getParser().getData(PacketSendOrReceiveParserData.class); - if (!acceptedTypes.isEmpty()) { - return acceptedTypes.toArray(new Class[0]); + if (getParser().isCurrentEvent(PacketSendOrReceiveEvent.class)) { + if (data.getProcessType() != EvtPacketSendOrReceive.ProcessType.NETTY) { + Skript.error("You can't alter packets in a " + (data.getProcessType() == null ? "unknown" : data.getProcessType().toString().toLowerCase(Locale.ENGLISH)) + " processed event, the packets have already been processed at that point. Use a netty processed event instead."); + return null; + } else if (data.getPriority() == PacketListenerPriority.MONITOR) { + Skript.error("You can't alter packets when using the \"monitor\" listening priority."); + return null; } } - return null; + + Class expected = this.fieldAccessor.expectedType(); + Class typeToAccept = expected.isArray() ? expected.getComponentType() : expected; + + return new Class[]{Primitives.wrap(typeToAccept)}; } - @SuppressWarnings({"unchecked", "UnstableApiUsage"}) + @SuppressWarnings({"unchecked"}) @Override public void change(Event event, Object[] delta, Changer.ChangeMode mode) { - if (mode != Changer.ChangeMode.SET || delta == null || delta.length == 0 || this.fieldName == null) return; + if (mode != Changer.ChangeMode.SET || delta == null || delta.length == 0) return; for (PacketWrapper wrapper : getExpr().getArray(event)) { if (wrapper == null) continue; - PacketTypeCommon type = wrapper.getPacketTypeData().getPacketType(); - FieldSchema> definition = PacketFieldRegistry.INSTANCE.getSchema(type); - - if (definition == null) continue; - - FieldAccessor, ?> targetField = definition.getAccessor(this.fieldName); - - if (targetField == null || targetField.setter() == null) continue; + FieldAccessor, ?> accessor = resolveAccessor(wrapper); + if (accessor == null || accessor.setter() == null) continue; Object newValue; - if (delta.length == 1) { - newValue = delta[0]; + Class expected = accessor.expectedType(); + + if (expected.isArray()) { + Class componentType = expected.getComponentType(); + Object typedArray = Array.newInstance(componentType, delta.length); + for (int i = 0; i < delta.length; i++) { + Array.set(typedArray, i, delta[i]); + } + newValue = typedArray; } else { - newValue = delta; // array + newValue = delta.length == 1 ? delta[0] : delta; } - Class expected = targetField.expectedType(); boolean isCompatible = expected == Object.class || expected.isInstance(newValue); - if (!isCompatible && expected.isArray() && newValue.getClass().isArray()) { isCompatible = true; } if (!isCompatible) { - Skript.warning("Cannot set the packet field '" + this.fieldName + "' to a value of type " + newValue.getClass().getSimpleName() + ". Expected type: " + expected.getSimpleName()); + Skript.warning("Cannot set the packet field '" + accessor.name() + "' to a value of type " + newValue.getClass().getSimpleName() + ". Expected type: " + expected.getSimpleName()); continue; } - ((BiConsumer, Object>) targetField.setter()).accept(wrapper, newValue); + ((BiConsumer, Object>) accessor.setter()).accept(wrapper, newValue); } } + @Override + public boolean isSingle() { + if (this.fieldAccessor == null) return true; + return getExpr().isSingle() && !this.fieldAccessor.expectedType().isArray(); + } + @Override public Class getReturnType() { - return Object.class; + if (this.fieldAccessor == null) return Object.class; + Class expected = this.fieldAccessor.expectedType(); + return expected.isArray() ? expected.getComponentType() : expected; } @Override public String toString(@Nullable Event event, boolean debug) { String wrapper = getExpr() != null ? getExpr().toString(event, debug) : "packet"; - return "packet field " + fieldName + " of " + wrapper; + String name = globalMode ? fieldName : this.fieldAccessor.name(); + return "packet field " + name + " of " + wrapper; } } \ No newline at end of file diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/ClientBoundPacketFieldRegistrar.java b/src/main/java/dev/threeadd/packeteventssk/element/general/field/ClientBoundPacketFieldRegistrar.java similarity index 99% rename from src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/ClientBoundPacketFieldRegistrar.java rename to src/main/java/dev/threeadd/packeteventssk/element/general/field/ClientBoundPacketFieldRegistrar.java index 9f15315..61a6fbb 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/ClientBoundPacketFieldRegistrar.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/general/field/ClientBoundPacketFieldRegistrar.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.general.field.packet; +package dev.threeadd.packeteventssk.element.general.field; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.retrooper.packetevents.protocol.packettype.PacketType; diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/PacketFieldRegistry.java b/src/main/java/dev/threeadd/packeteventssk/element/general/field/PacketFieldRegistry.java similarity index 96% rename from src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/PacketFieldRegistry.java rename to src/main/java/dev/threeadd/packeteventssk/element/general/field/PacketFieldRegistry.java index c81d90b..baf6ed8 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/PacketFieldRegistry.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/general/field/PacketFieldRegistry.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.general.field.packet; +package dev.threeadd.packeteventssk.element.general.field; import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon; import com.github.retrooper.packetevents.wrapper.PacketWrapper; diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/ServerBoundPacketFieldRegistrar.java b/src/main/java/dev/threeadd/packeteventssk/element/general/field/ServerBoundPacketFieldRegistrar.java similarity index 67% rename from src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/ServerBoundPacketFieldRegistrar.java rename to src/main/java/dev/threeadd/packeteventssk/element/general/field/ServerBoundPacketFieldRegistrar.java index 118d608..73587c9 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/ServerBoundPacketFieldRegistrar.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/general/field/ServerBoundPacketFieldRegistrar.java @@ -1,11 +1,13 @@ -package dev.threeadd.packeteventssk.element.general.field.packet; +package dev.threeadd.packeteventssk.element.general.field; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.protocol.player.InteractionHand; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientSelectBundleItem; +import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUpdateSign; import dev.threeadd.packeteventssk.api.util.ConversionUtil; import dev.threeadd.packeteventssk.api.util.field.FieldRegistrar; +import org.bukkit.block.sign.Side; import org.bukkit.util.Vector; public class ServerBoundPacketFieldRegistrar implements FieldRegistrar { @@ -44,5 +46,24 @@ public void register() { values.getRequired("sneaking state", Boolean.class) )) .build(); + + PacketFieldRegistry.INSTANCE.builder(PacketType.Play.Client.UPDATE_SIGN, WrapperPlayClientUpdateSign.class) + .requiredField(Vector.class, + w -> ConversionUtil.toBukkitVector(w.getBlockPosition()), + (w, newPos) -> w.setBlockPosition(ConversionUtil.toPeVectorI(newPos)), + "location vector", "location") + .requiredField(String[].class, + WrapperPlayClientUpdateSign::getTextLines, + WrapperPlayClientUpdateSign::setTextLines, + "sign lines", "lines") + .requiredField(Side.class, w -> w.isFrontText() ? Side.FRONT : Side.BACK, + (w, side) -> w.setFrontText(side == Side.FRONT), + "sign side", "side") + .constructor(values -> new WrapperPlayClientUpdateSign( + ConversionUtil.toPeVectorI(values.getRequired("location vector", Vector.class)), + values.getRequired("sign lines", String[].class), + values.getRequired("sign side", Side.class) == Side.FRONT + )) + .build(); } } diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/SkBeePacketFieldRegistrar.java b/src/main/java/dev/threeadd/packeteventssk/element/general/field/SkBeePacketFieldRegistrar.java similarity index 97% rename from src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/SkBeePacketFieldRegistrar.java rename to src/main/java/dev/threeadd/packeteventssk/element/general/field/SkBeePacketFieldRegistrar.java index 39a43c4..8a738ef 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/field/packet/SkBeePacketFieldRegistrar.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/general/field/SkBeePacketFieldRegistrar.java @@ -1,4 +1,4 @@ -package dev.threeadd.packeteventssk.element.general.field.packet; +package dev.threeadd.packeteventssk.element.general.field; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.protocol.world.blockentity.BlockEntityType; diff --git a/src/main/java/dev/threeadd/packeteventssk/element/general/section/SecExprNewPacket.java b/src/main/java/dev/threeadd/packeteventssk/element/general/section/SecExprNewPacket.java index 5f6d4db..16ece15 100644 --- a/src/main/java/dev/threeadd/packeteventssk/element/general/section/SecExprNewPacket.java +++ b/src/main/java/dev/threeadd/packeteventssk/element/general/section/SecExprNewPacket.java @@ -15,7 +15,7 @@ import dev.threeadd.packeteventssk.api.util.field.ConstructionContext; import dev.threeadd.packeteventssk.api.util.field.FieldAccessor; import dev.threeadd.packeteventssk.api.util.field.FieldSchema; -import dev.threeadd.packeteventssk.element.general.field.packet.PacketFieldRegistry; +import dev.threeadd.packeteventssk.element.general.field.PacketFieldRegistry; import org.bukkit.event.Event; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,22 +26,22 @@ public class SecExprNewPacket extends SectionExpression> { - private static EntryValidator VALIDATOR; + private static final Map>, EntryValidator> VALIDATORS = new HashMap<>(); @SuppressWarnings({"unchecked", "rawtypes"}) public static void register(Registration reg) { - SimpleEntryValidator builder = SimpleEntryValidator.builder(); for (FieldSchema> def : PacketFieldRegistry.INSTANCE.getAllSchemas()) { + SimpleEntryValidator builder = SimpleEntryValidator.builder(); for (FieldAccessor, ?> field : def.accessors()) { builder.addOptionalEntry(field.name(), Object.class); - for (String alias : field.aliases()) { // register aliases + for (String alias : field.aliases()) { builder.addOptionalEntry(alias, Object.class); } } + VALIDATORS.put(def, builder.build()); } - VALIDATOR = builder.build(); StringBuilder description = new StringBuilder(); description.append("Create a new packet from a packet type.\n\n"); @@ -52,7 +52,7 @@ public static void register(Registration reg) { String fieldLines = schema.getReadableFields(); if (!fieldLines.isEmpty()) { description.append("* **") - .append(schema.type().toString().toLowerCase(Locale.ENGLISH).replace("_", " ")) + .append(schema.type().getName().toLowerCase(Locale.ENGLISH).replace("_", " ")) .append("** fields:\n") .append(fieldLines) .append("\n"); @@ -62,7 +62,14 @@ public static void register(Registration reg) { reg.newSimpleExpression(SecExprNewPacket.class, (Class) PacketWrapper.class, "[a] [new] %*packettype%") .name("General - New Packet") .description(description.toString()) - // TODO example + .examples(""" + command killTargetForMe: + trigger: + set {_packet} to a new clientbound destroy entities packet: + entity ids: protocol id of target entity + + silently send packet {_packet} to the player + """) .since("1.0.0", "1.1.0 (changed to SectionExpression) and large changes") .register(); } @@ -92,21 +99,27 @@ public boolean init(Expression[] expressions, this.schema = PacketFieldRegistry.INSTANCE.getSchema(this.type); if (this.schema == null) { - Skript.error("Packet creation for " + this.type.getName() + " is not currently supported. Consider creating/handling it through reflection."); + Skript.error("Packet creation for " + this.type.getName().toLowerCase(Locale.ENGLISH).replace("_", " ") + " is not currently supported. Consider creating/handling it through reflection."); return false; // can't return an empty packet so this expr can't be used } boolean hasRequiredFields = this.schema.accessors().stream().anyMatch(field -> !field.isOptional()); if (sectionNode == null) { if (hasRequiredFields) { - Skript.error("You must provide a section with the required fields to create a " + this.type.getName() + " packet."); + Skript.error("You must provide a section with the required fields to create a " + this.type.getName().toLowerCase(Locale.ENGLISH).replace("_", " ") + " packet."); return false; } return true; } - EntryContainer container = VALIDATOR.validate(sectionNode); + EntryValidator validator = VALIDATORS.get(this.schema); + if (validator == null) { + Skript.error("No validator found for " + this.type.getName().toLowerCase(Locale.ENGLISH).replace("_", " ") + " packet."); + return false; + } + + EntryContainer container = validator.validate(sectionNode); if (container == null) { return false; } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index d1208c4..1d6f38c 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,4 +1,4 @@ -# Welcome to PacketEventsSK v1.1.2 +# Welcome to PacketEventsSK v1.1.3 # If you have any questions, please first refer to the wiki: # - https://github.com/3add/PacketEventsSK/wiki