From e537836ad0bda6960036ba935c65a69b0a8215b7 Mon Sep 17 00:00:00 2001 From: SirSmurfy2 <82696841+TheAbsolutionism@users.noreply.github.com> Date: Sat, 1 Feb 2025 12:46:05 -0500 Subject: [PATCH 01/15] Remove Some Redundant "The"s in syntax (#7559) --- .../java/ch/njol/skript/expressions/ExprExperienceCooldown.java | 2 +- .../skript/expressions/ExprExperienceCooldownChangeReason.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/njol/skript/expressions/ExprExperienceCooldown.java b/src/main/java/ch/njol/skript/expressions/ExprExperienceCooldown.java index 4fc90490348..f28d8f3b36b 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprExperienceCooldown.java +++ b/src/main/java/ch/njol/skript/expressions/ExprExperienceCooldown.java @@ -29,7 +29,7 @@ public class ExprExperienceCooldown extends SimplePropertyExpression { static { - register(ExprExperienceCooldown.class, Timespan.class, "[the] (experience|[e]xp) [pickup|collection] cooldown", "players"); + register(ExprExperienceCooldown.class, Timespan.class, "(experience|[e]xp) [pickup|collection] cooldown", "players"); } private static final int maxTicks = Integer.MAX_VALUE; diff --git a/src/main/java/ch/njol/skript/expressions/ExprExperienceCooldownChangeReason.java b/src/main/java/ch/njol/skript/expressions/ExprExperienceCooldownChangeReason.java index 4cb4570a7eb..a0e569e1e3b 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprExperienceCooldownChangeReason.java +++ b/src/main/java/ch/njol/skript/expressions/ExprExperienceCooldownChangeReason.java @@ -25,7 +25,7 @@ public class ExprExperienceCooldownChangeReason extends EventValueExpression { static { - register(ExprExperienceCooldownChangeReason.class, ChangeReason.class, "[the] (experience|[e]xp) cooldown change (reason|cause|type)"); + register(ExprExperienceCooldownChangeReason.class, ChangeReason.class, "(experience|[e]xp) cooldown change (reason|cause|type)"); } public ExprExperienceCooldownChangeReason() { From c742acf57c18920a3789c18f771f9200d54bce48 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 1 Feb 2025 13:00:34 -0500 Subject: [PATCH 02/15] Removes no-keyword pattern from ExprNewBannerPattern (#7546) --- .../expressions/ExprNewBannerPattern.java | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/main/java/ch/njol/skript/expressions/ExprNewBannerPattern.java b/src/main/java/ch/njol/skript/expressions/ExprNewBannerPattern.java index cbbf8cf83d9..b4d4f39ea62 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprNewBannerPattern.java +++ b/src/main/java/ch/njol/skript/expressions/ExprNewBannerPattern.java @@ -24,34 +24,23 @@ "remove {_pattern} from banner patterns of {_banneritem}", "set the 1st banner pattern of block at location(0,0,0) to {_pattern}", "clear the 1st banner pattern of block at location(0,0,0)", - "", - "set {_pattern} to a red mojang banner pattern" }) @Since("2.10") public class ExprNewBannerPattern extends SimpleExpression { static { - Skript.registerExpression(ExprNewBannerPattern.class, Pattern.class, ExpressionType.PATTERN_MATCHES_EVERYTHING, - "[a] %bannerpatterntype% colo[u]red %color%", - "[a] %*color% %bannerpatterntype%"); + Skript.registerExpression(ExprNewBannerPattern.class, Pattern.class, ExpressionType.COMBINED, + "[a] %bannerpatterntype% colo[u]red %color%"); } private Expression selectedPattern; private Expression selectedColor; @Override + @SuppressWarnings("unchecked") public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (matchedPattern == 0) { - //noinspection unchecked - selectedPattern = (Expression) exprs[0]; - //noinspection unchecked - selectedColor = (Expression) exprs[1]; - } else { - //noinspection unchecked - selectedPattern = (Expression) exprs[1]; - //noinspection unchecked - selectedColor = (Expression) exprs[0]; - } + selectedPattern = (Expression) exprs[0]; + selectedColor = (Expression) exprs[1]; return true; } From 5adda3624134dc2a48e4fd0bf152dfe946972ebc Mon Sep 17 00:00:00 2001 From: burbulinis <131194155+Burbulinis@users.noreply.github.com> Date: Sat, 1 Feb 2025 20:34:49 +0200 Subject: [PATCH 03/15] Fix Invalid Equipment Slots in CondIsWearing (#7525) --- .../njol/skript/conditions/CondIsWearing.java | 21 +++++- ...issingCheckIfEntityCanUseSlot7524Test.java | 64 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/skriptlang/skript/test/tests/regression/MissingCheckIfEntityCanUseSlot7524Test.java diff --git a/src/main/java/ch/njol/skript/conditions/CondIsWearing.java b/src/main/java/ch/njol/skript/conditions/CondIsWearing.java index ece4ba03a29..3b48d4eec2d 100644 --- a/src/main/java/ch/njol/skript/conditions/CondIsWearing.java +++ b/src/main/java/ch/njol/skript/conditions/CondIsWearing.java @@ -1,5 +1,6 @@ package ch.njol.skript.conditions; +import ch.njol.skript.Skript; import ch.njol.skript.aliases.ItemType; import ch.njol.skript.conditions.base.PropertyCondition; import ch.njol.skript.conditions.base.PropertyCondition.PropertyType; @@ -12,7 +13,7 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; -import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.*; import org.bukkit.event.Event; import org.bukkit.inventory.EntityEquipment; import org.bukkit.inventory.EquipmentSlot; @@ -30,6 +31,9 @@ }) @Since("1.0") public class CondIsWearing extends Condition { + + private static final boolean HAS_CAN_USE_SLOT_METHOD = Skript.methodExists(LivingEntity.class, "canUseEquipmentSlot", EquipmentSlot.class); + private static final boolean HAS_BODY_SLOT = Skript.fieldExists(EquipmentSlot.class, "BODY"); static { PropertyCondition.register(CondIsWearing.class, "wearing %itemtypes%", "livingentities"); @@ -59,6 +63,21 @@ public boolean check(Event event) { return false; // spigot nullability, no identifier as to why this occurs ItemStack[] contents = Arrays.stream(EquipmentSlot.values()) + .filter(slot -> { + // this method was added in 1.20.6 + if (HAS_CAN_USE_SLOT_METHOD) + return entity.canUseEquipmentSlot(slot); + + // body slot was added in 1.20.5 + if (HAS_BODY_SLOT && slot == EquipmentSlot.BODY) + // this may change in the future, but for now this is the only way to figure out + // if the entity can use the body slot + return entity instanceof Horse + || entity instanceof Wolf + || entity instanceof Llama; + + return true; + }) .map(equipment::getItem) .toArray(ItemStack[]::new); diff --git a/src/test/java/org/skriptlang/skript/test/tests/regression/MissingCheckIfEntityCanUseSlot7524Test.java b/src/test/java/org/skriptlang/skript/test/tests/regression/MissingCheckIfEntityCanUseSlot7524Test.java new file mode 100644 index 00000000000..f0f4af67864 --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/regression/MissingCheckIfEntityCanUseSlot7524Test.java @@ -0,0 +1,64 @@ +package org.skriptlang.skript.test.tests.regression; + +import ch.njol.skript.Skript; +import ch.njol.skript.lang.Condition; +import ch.njol.skript.lang.util.ContextlessEvent; +import ch.njol.skript.test.runner.SkriptJUnitTest; +import ch.njol.skript.variables.Variables; +import org.bukkit.Material; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class MissingCheckIfEntityCanUseSlot7524Test extends SkriptJUnitTest { + + private static final boolean HAS_CAN_USE_SLOT_METHOD = Skript.methodExists(LivingEntity.class, "canUseEquipmentSlot", EquipmentSlot.class); + + private Player player; + private EntityEquipment equipment; + private Condition isWearingCondition; + + @Before + public void setup() { + player = EasyMock.niceMock(Player.class); + equipment = EasyMock.niceMock(EntityEquipment.class); + + isWearingCondition = Condition.parse("{_player} is wearing diamond chestplate", null); + if (isWearingCondition == null) + throw new IllegalStateException(); + } + + @Test + public void test() { + ContextlessEvent event = ContextlessEvent.get(); + Variables.setVariable("player", player, event, true); + + EasyMock.expect(player.isValid()).andStubReturn(true); + EasyMock.expect(player.getEquipment()).andReturn(equipment); + + if (HAS_CAN_USE_SLOT_METHOD) { + EasyMock.expect(player.canUseEquipmentSlot(EquipmentSlot.CHEST)).andReturn(true); + EasyMock.expect(player.canUseEquipmentSlot(EquipmentSlot.LEGS)).andReturn(true); + EasyMock.expect(player.canUseEquipmentSlot(EquipmentSlot.FEET)).andReturn(true); + EasyMock.expect(player.canUseEquipmentSlot(EquipmentSlot.HEAD)).andReturn(true); + EasyMock.expect(player.canUseEquipmentSlot(EquipmentSlot.HAND)).andReturn(true); + EasyMock.expect(player.canUseEquipmentSlot(EquipmentSlot.OFF_HAND)).andReturn(true); + EasyMock.expect(player.canUseEquipmentSlot(EquipmentSlot.BODY)).andReturn(false); + } + + EasyMock.expect(equipment.getItem(EquipmentSlot.CHEST)).andReturn(new ItemStack(Material.DIAMOND_CHESTPLATE)); + + EasyMock.replay(player, equipment); + + assert isWearingCondition.check(event); + + EasyMock.verify(player, equipment); + } + +} From 382f883fd8d9a72c279f4dcb621d2a1ea27c50a4 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 1 Feb 2025 13:43:16 -0500 Subject: [PATCH 04/15] Fix cast exception when using invalid classes with CondIsTagged (#7527) --- .../skript/bukkit/tags/elements/CondIsTagged.java | 7 ++++++- src/test/skript/tests/syntaxes/conditions/CondIsTagged.sk | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/skriptlang/skript/bukkit/tags/elements/CondIsTagged.java b/src/main/java/org/skriptlang/skript/bukkit/tags/elements/CondIsTagged.java index 3741bbb9d68..0f8d53b7439 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/tags/elements/CondIsTagged.java +++ b/src/main/java/org/skriptlang/skript/bukkit/tags/elements/CondIsTagged.java @@ -60,10 +60,15 @@ public boolean check(Event event) { return elements.check(event, element -> { boolean isAny = (element instanceof ItemType itemType && !itemType.isAll()); Keyed[] values = TagModule.getKeyed(element); - if (values == null) + if (values == null || values.length == 0) return false; + Class valueClass = values[0].getClass(); + for (Tag tag : tags) { + // cursed check to ensure the tag is the same type as the values + if (!tag.getValues().iterator().next().getClass().isAssignableFrom(valueClass)) + return false; if (isTagged(tag, values, !isAny)) { if (!and) return true; diff --git a/src/test/skript/tests/syntaxes/conditions/CondIsTagged.sk b/src/test/skript/tests/syntaxes/conditions/CondIsTagged.sk index 7b6ec7b5638..e6942be69cb 100644 --- a/src/test/skript/tests/syntaxes/conditions/CondIsTagged.sk +++ b/src/test/skript/tests/syntaxes/conditions/CondIsTagged.sk @@ -13,4 +13,7 @@ test "CondIsTagged": spawn a skeleton at spawn of world "world": assert entity is tagged as entity tag "minecraft:skeletons" with "skeleton is not a skeleton" + assert entity is not tagged as item tag "doors" with "failed to handle checking entity for item tag" delete entity + + assert stone sword is not tagged as entity tag "skeletons" with "failed to handle checking item for entity tag" From f7eb560bfbbb64d213293a982684203a2c97f4f8 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Sat, 1 Feb 2025 13:48:10 -0500 Subject: [PATCH 05/15] Fix Date Serialization (#7570) --- .../ch/njol/skript/classes/data/SkriptClasses.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java index edae0fba4ad..ee44ad09dd2 100644 --- a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java @@ -354,15 +354,19 @@ public String toVariableNameString(final Timeperiod o) { @Override public Fields serialize(Date date) { Fields fields = new Fields(); - fields.putPrimitive("time", date.getTime()); + fields.putPrimitive("timestamp", date.getTime()); return fields; } @Override - protected Date deserialize(Fields fields) - throws StreamCorruptedException { - long time = fields.getPrimitive("time", long.class); + protected Date deserialize(Fields fields) throws StreamCorruptedException { + long time; + if (fields.hasField("time")) { // compatibility for 2.10.0 (#7542) + time = fields.getPrimitive("time", long.class); + } else { + time = fields.getPrimitive("timestamp", long.class); + } return new Date(time); } From 16e19535e6bb1b6bd364939226e47878be9b1059 Mon Sep 17 00:00:00 2001 From: Eren <67760502+erenkarakal@users.noreply.github.com> Date: Sat, 1 Feb 2025 21:51:45 +0300 Subject: [PATCH 06/15] Force Test Server Locale to English (#7557) --- src/main/java/ch/njol/skript/test/platform/Environment.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/ch/njol/skript/test/platform/Environment.java b/src/main/java/ch/njol/skript/test/platform/Environment.java index 907265db2e1..ecd41b32944 100644 --- a/src/main/java/ch/njol/skript/test/platform/Environment.java +++ b/src/main/java/ch/njol/skript/test/platform/Environment.java @@ -231,6 +231,8 @@ public TestResults runTests(Path runnerRoot, Path testsRoot, boolean devMode, bo args.add("-Ddisable.watchdog=true"); if (debug) args.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000"); + args.add("-Duser.language=en"); + args.add("-Duser.country=US"); args.addAll(jvmArgs); args.addAll(Arrays.asList(commandLine)); From 757b5e72440d2c98e3daced5ec9e5606d0e0c08c Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Sat, 1 Feb 2025 10:55:53 -0800 Subject: [PATCH 07/15] RegistryParser - fix pulling from the lang file on custom entries (#7540) --- .../java/ch/njol/skript/classes/registry/RegistryParser.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/ch/njol/skript/classes/registry/RegistryParser.java b/src/main/java/ch/njol/skript/classes/registry/RegistryParser.java index ba35553f297..d7a1a84e81a 100644 --- a/src/main/java/ch/njol/skript/classes/registry/RegistryParser.java +++ b/src/main/java/ch/njol/skript/classes/registry/RegistryParser.java @@ -47,7 +47,7 @@ private void refresh() { String namespace = namespacedKey.getNamespace(); String key = namespacedKey.getKey(); String keyWithSpaces = key.replace("_", " "); - String languageKey = languageNode + "." + key; + String languageKey; // Put the full namespaced key as a pattern parseMap.put(namespacedKey.toString(), registryObject); @@ -55,6 +55,9 @@ private void refresh() { // If the object is a vanilla Minecraft object, we'll add the key with spaces as a pattern if (namespace.equalsIgnoreCase(NamespacedKey.MINECRAFT)) { parseMap.put(keyWithSpaces, registryObject); + languageKey = languageNode + "." + key; + } else { + languageKey = namespacedKey.toString(); } String[] options = Language.getList(languageKey); From b4db4b1946baad5da66c3fe5d85f67d9cc7f4ce5 Mon Sep 17 00:00:00 2001 From: burbulinis <131194155+Burbulinis@users.noreply.github.com> Date: Sat, 1 Feb 2025 21:01:04 +0200 Subject: [PATCH 08/15] Add a LootTable Serializer (#7528) --- .../bukkit/loottables/LootTableModule.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/main/java/org/skriptlang/skript/bukkit/loottables/LootTableModule.java b/src/main/java/org/skriptlang/skript/bukkit/loottables/LootTableModule.java index 25770b763cf..a11fdb1a16d 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/loottables/LootTableModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/loottables/LootTableModule.java @@ -3,11 +3,13 @@ import ch.njol.skript.Skript; import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.classes.Parser; +import ch.njol.skript.classes.Serializer; import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.lang.util.SimpleEvent; import ch.njol.skript.registrations.Classes; import ch.njol.skript.registrations.EventValues; +import ch.njol.yggdrasil.Fields; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.NamespacedKey; @@ -18,6 +20,7 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.io.StreamCorruptedException; public class LootTableModule { @@ -53,6 +56,42 @@ public String toVariableNameString(LootTable o) { return "loot table:" + o.getKey(); } }) + .serializer(new Serializer<>() { + @Override + public Fields serialize(LootTable lootTable) { + Fields fields = new Fields(); + fields.putObject("key", lootTable.getKey().toString()); + return fields; + } + + @Override + public void deserialize(LootTable lootTable, Fields fields) { + assert false; + } + + @Override + protected LootTable deserialize(Fields fields) throws StreamCorruptedException { + String key = fields.getAndRemoveObject("key", String.class); + if (key == null) + throw new StreamCorruptedException(); + + NamespacedKey namespacedKey = NamespacedKey.fromString(key); + if (namespacedKey == null) + throw new StreamCorruptedException(); + + return Bukkit.getLootTable(namespacedKey); + } + + @Override + public boolean mustSyncDeserialization() { + return true; + } + + @Override + protected boolean canBeInstantiated() { + return false; + } + }) ); Classes.registerClass(new ClassInfo<>(LootContext.class, "lootcontext") From ed09beff0e852b97a0086966a802891013192084 Mon Sep 17 00:00:00 2001 From: Phill310 Date: Sat, 1 Feb 2025 11:04:33 -0800 Subject: [PATCH 09/15] Make examples use proper syntax (#7521) --- .../skript/bukkit/fishing/elements/CondFishingLure.java | 4 ++-- .../bukkit/fishing/elements/ExprFishingApproachAngle.java | 5 ++++- .../skript/bukkit/fishing/elements/ExprFishingBiteTime.java | 2 +- .../skript/bukkit/fishing/elements/ExprFishingWaitTime.java | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/CondFishingLure.java b/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/CondFishingLure.java index 6a0ebf0b418..215ad63bf92 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/CondFishingLure.java +++ b/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/CondFishingLure.java @@ -14,7 +14,7 @@ @Description("Checks if the lure enchantment is applied to the current fishing event.") @Examples({ "on fishing line cast:", - "\tif lure enchantment is applied:", + "\tif lure enchantment bonus is applied:", "\t\tcancel event" }) @Events("Fishing") @@ -49,7 +49,7 @@ public boolean check(Event event) { @Override public String toString(@Nullable Event event, boolean debug) { - return "lure enchantment " + (isNegated() ? "is" : "isn't") + " applied"; + return "lure enchantment bonus " + (isNegated() ? "is" : "isn't") + " applied"; } } diff --git a/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingApproachAngle.java b/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingApproachAngle.java index 1d7b920b596..3ac1754af25 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingApproachAngle.java +++ b/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingApproachAngle.java @@ -21,7 +21,10 @@ }) @Examples({ "on fish approach:", - "\tif fishing approach angle is bigger than 300.5 degrees or fishing approach angle is smaller than 59.5 degrees:", + "\tif any:", + "\t\tmaximum fishing approach angle is bigger than 300.5 degrees", + "\t\tmin fishing approach angle is smaller than 59.5 degrees", + "\tthen:", "\t\tcancel event" }) @Events("Fishing") diff --git a/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingBiteTime.java b/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingBiteTime.java index 77738e9d743..78f7b3f5129 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingBiteTime.java +++ b/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingBiteTime.java @@ -21,7 +21,7 @@ }) @Examples({ "on fish approach:", - "\tset fishing bite time to 5 seconds", + "\tset fishing bite time to 5 seconds", }) @RequiredPlugins("Paper 1.20.6") @Events("Fishing") diff --git a/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingWaitTime.java b/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingWaitTime.java index b71552571f5..0358a2ee554 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingWaitTime.java +++ b/src/main/java/org/skriptlang/skript/bukkit/fishing/elements/ExprFishingWaitTime.java @@ -21,8 +21,8 @@ }) @Examples({ "on fishing line cast:", - "\tset min waiting time to 10 seconds", - "\tset max waiting time to 20 seconds", + "\tset min fish waiting time to 10 seconds", + "\tset max fishing waiting time to 20 seconds", }) @Events("Fishing") @Since("2.10") From aa193934ec40035722cb8b9063bb6277ce67ab05 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 1 Feb 2025 14:08:04 -0500 Subject: [PATCH 10/15] Fix getArray mistake in CondIsTagged, add runtime error for bad namespace keys in ExprTag (#7474) --- .../bukkit/tags/elements/CondIsTagged.java | 2 +- .../skript/bukkit/tags/elements/ExprTag.java | 30 ++++++++++++++----- .../regressions/7471-is tagged or list.sk | 6 ++++ 3 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 src/test/skript/tests/regressions/7471-is tagged or list.sk diff --git a/src/main/java/org/skriptlang/skript/bukkit/tags/elements/CondIsTagged.java b/src/main/java/org/skriptlang/skript/bukkit/tags/elements/CondIsTagged.java index 0f8d53b7439..4f396df08f5 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/tags/elements/CondIsTagged.java +++ b/src/main/java/org/skriptlang/skript/bukkit/tags/elements/CondIsTagged.java @@ -53,7 +53,7 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is @Override public boolean check(Event event) { - Tag[] tags = this.tags.getArray(event); + Tag[] tags = this.tags.getAll(event); if (tags.length == 0) return isNegated(); boolean and = this.tags.getAnd(); diff --git a/src/main/java/org/skriptlang/skript/bukkit/tags/elements/ExprTag.java b/src/main/java/org/skriptlang/skript/bukkit/tags/elements/ExprTag.java index 8c990e6b85e..9b39b1508f4 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/tags/elements/ExprTag.java +++ b/src/main/java/org/skriptlang/skript/bukkit/tags/elements/ExprTag.java @@ -1,6 +1,7 @@ package org.skriptlang.skript.bukkit.tags.elements; import ch.njol.skript.Skript; +import ch.njol.skript.config.Node; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Keywords; @@ -22,6 +23,7 @@ import org.skriptlang.skript.bukkit.tags.TagModule; import org.skriptlang.skript.bukkit.tags.TagType; import org.skriptlang.skript.bukkit.tags.sources.TagOrigin; +import org.skriptlang.skript.log.runtime.SyntaxRuntimeErrorProducer; import java.util.ArrayList; import java.util.List; @@ -51,7 +53,7 @@ @Since("2.10") @RequiredPlugins("Paper (paper tags)") @Keywords({"blocks", "minecraft tag", "type", "category"}) -public class ExprTag extends SimpleExpression { +public class ExprTag extends SimpleExpression implements SyntaxRuntimeErrorProducer { static { Skript.registerExpression(ExprTag.class, Tag.class, ExpressionType.COMBINED, @@ -63,6 +65,8 @@ public class ExprTag extends SimpleExpression { private TagOrigin origin; private boolean datapackOnly; + private Node node; + @Override public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { //noinspection unchecked @@ -70,6 +74,7 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is types = TagType.fromParseMark(parseResult.mark); origin = TagOrigin.fromParseTags(parseResult.tags); datapackOnly = origin == TagOrigin.BUKKIT && parseResult.hasTag("datapack"); + node = getParser().getNode(); return true; } @@ -87,14 +92,20 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is nextName: for (String name : names) { // get key NamespacedKey key; - if (name.contains(":")) { - key = NamespacedKey.fromString(name); - } else { - // populate namespace if not provided - key = new NamespacedKey(namespace, name); + try { + if (name.contains(":")) { + key = NamespacedKey.fromString(name); + } else { + // populate namespace if not provided + key = new NamespacedKey(namespace, name); + } + } catch (IllegalArgumentException e) { + key = null; } - if (key == null) + if (key == null) { + error("Invalid tag key: '" + name + "'. Tags may only contain a-z, 0-9, _, ., /, or - characters."); continue; + } Tag tag; for (TagType type : types) { @@ -122,6 +133,11 @@ public Class getReturnType() { return Tag.class; } + @Override + public Node getNode() { + return node; + } + @Override public String toString(@Nullable Event event, boolean debug) { String registry = types.length > 1 ? "" : " " + types[0].toString(); diff --git a/src/test/skript/tests/regressions/7471-is tagged or list.sk b/src/test/skript/tests/regressions/7471-is tagged or list.sk new file mode 100644 index 00000000000..bbf1d2f1480 --- /dev/null +++ b/src/test/skript/tests/regressions/7471-is tagged or list.sk @@ -0,0 +1,6 @@ +test "is tagged or list": + register a custom item tag named "or_list_tag_1" using oak log + register a custom item tag named "or_list_tag_2" using spruce log + + loop 10 times: + assert oak log is tagged as custom tag "or_list_tag_1" or custom tag "or_list_tag_2" From c2788057ed32dbaadba5ab2f3d49b9ea01266762 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 1 Feb 2025 14:32:01 -0500 Subject: [PATCH 11/15] Enable regex-based highlighting for runtime errors (#7526) --- .../skript/bukkit/log/runtime/BukkitRuntimeErrorConsumer.java | 2 +- .../java/org/skriptlang/skript/log/runtime/RuntimeError.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/skriptlang/skript/bukkit/log/runtime/BukkitRuntimeErrorConsumer.java b/src/main/java/org/skriptlang/skript/bukkit/log/runtime/BukkitRuntimeErrorConsumer.java index bd9b2201eb3..82458865dac 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/log/runtime/BukkitRuntimeErrorConsumer.java +++ b/src/main/java/org/skriptlang/skript/bukkit/log/runtime/BukkitRuntimeErrorConsumer.java @@ -67,7 +67,7 @@ public void printError(@NotNull RuntimeError error) { ErrorSource source = error.source(); String code = source.lineText(); if (error.toHighlight() != null && !error.toHighlight().isEmpty()) - code = code.replace("§", "&").replace(error.toHighlight(), "§f§n" + error.toHighlight() + "§7"); + code = code.replace("§", "&").replaceFirst(error.toHighlight(), "§f§n$0§7"); SkriptLogger.sendFormatted(Bukkit.getConsoleSender(), String.format(skriptInfo, source.script(), source.syntaxName(), source.syntaxType()) + diff --git a/src/main/java/org/skriptlang/skript/log/runtime/RuntimeError.java b/src/main/java/org/skriptlang/skript/log/runtime/RuntimeError.java index 2b99be73382..cd66458525f 100644 --- a/src/main/java/org/skriptlang/skript/log/runtime/RuntimeError.java +++ b/src/main/java/org/skriptlang/skript/log/runtime/RuntimeError.java @@ -10,5 +10,6 @@ * @param source The source of the error * @param error The message to display as the error * @param toHighlight Optionally, the text within the emitting line to highlight. + * This should be treated as a regex pattern and will highlight the first match it finds. */ public record RuntimeError(Level level, ErrorSource source, String error, @Nullable String toHighlight) { } From deb21b5515df4e6f3323481503d030e6d5dbc0ae Mon Sep 17 00:00:00 2001 From: Fusezion Date: Sat, 1 Feb 2025 14:41:16 -0500 Subject: [PATCH 12/15] Fix tag lookup for "skript" and "paper" (#7450) --------- Co-authored-by: Patrick Miller --- .../skript/bukkit/tags/elements/ExprTag.java | 57 +++++++++++-------- .../7449-tag lookup only does minecraft.sk | 5 ++ 2 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 src/test/skript/tests/regressions/7449-tag lookup only does minecraft.sk diff --git a/src/main/java/org/skriptlang/skript/bukkit/tags/elements/ExprTag.java b/src/main/java/org/skriptlang/skript/bukkit/tags/elements/ExprTag.java index 9b39b1508f4..c812f564dbd 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/tags/elements/ExprTag.java +++ b/src/main/java/org/skriptlang/skript/bukkit/tags/elements/ExprTag.java @@ -80,46 +80,55 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is @Override protected Tag @Nullable [] get(Event event) { - String[] names = this.names.getArray(event); List> tags = new ArrayList<>(); - String namespace = switch (origin) { - case ANY, BUKKIT -> "minecraft"; - case PAPER -> "paper"; - case SKRIPT -> "skript"; + String[] namespaces = switch (origin) { + case ANY -> new String[]{"minecraft", "paper", "skript"}; + case BUKKIT -> new String[]{"minecraft"}; + case PAPER -> new String[]{"paper"}; + case SKRIPT -> new String[]{"skript"}; }; - nextName: for (String name : names) { - // get key - NamespacedKey key; + nextName: for (String name : this.names.getArray(event)) { + boolean invalidKey = false; try { if (name.contains(":")) { - key = NamespacedKey.fromString(name); + NamespacedKey key = NamespacedKey.fromString(name); + invalidKey = key == null; + if (!invalidKey) { + tags.add(findTag(key)); + } } else { - // populate namespace if not provided - key = new NamespacedKey(namespace, name); + for (String namespace : namespaces) { + Tag tag = findTag(new NamespacedKey(namespace, name)); + if (tag != null) { + tags.add(tag); + continue nextName; + } + } } } catch (IllegalArgumentException e) { - key = null; + invalidKey = true; } - if (key == null) { + if (invalidKey) { error("Invalid tag key: '" + name + "'. Tags may only contain a-z, 0-9, _, ., /, or - characters."); continue; } + } + return tags.toArray(Tag[]::new); + } - Tag tag; - for (TagType type : types) { - tag = TagModule.tagRegistry.getTag(origin, type, key); - if (tag != null - // ensures that only datapack/minecraft tags are sent when specifically requested - && (origin != TagOrigin.BUKKIT || (datapackOnly ^ tag.getKey().getNamespace().equals("minecraft"))) - ) { - tags.add(tag); - continue nextName; // ensure 1:1 - } + private @Nullable Tag findTag(NamespacedKey key) { + for (TagType type : types) { + Tag tag = TagModule.tagRegistry.getTag(origin, type, key); + if (tag != null + // ensures that only datapack/minecraft tags are sent when specifically requested + && (origin != TagOrigin.BUKKIT || (datapackOnly ^ tag.getKey().getNamespace().equals(NamespacedKey.MINECRAFT))) + ) { + return tag; } } - return tags.toArray(new Tag[0]); + return null; } @Override diff --git a/src/test/skript/tests/regressions/7449-tag lookup only does minecraft.sk b/src/test/skript/tests/regressions/7449-tag lookup only does minecraft.sk new file mode 100644 index 00000000000..ad830f21fa0 --- /dev/null +++ b/src/test/skript/tests/regressions/7449-tag lookup only does minecraft.sk @@ -0,0 +1,5 @@ +test "7449 - tag lookup only uses minecraft namespace": + register an item tag named "my_favorite_blocks" using oak log, stone, and podzol + assert tag "my_favorite_blocks" is tag "skript:my_favorite_blocks" with "Tag lookup didn't find a skript tag ""helmets"" namespace" + assert tag "helmets" is tag "paper:helmets" with "Tag lookup didn't find a paper tag ""helmets"" namespace" + assert tag "dirt" is tag "minecraft:dirt" with "Tag lookup didn't find a minecraft tag ""dirt"" namespace" From 2bccb3232f8da54cece796ba277d1355f8a83d0b Mon Sep 17 00:00:00 2001 From: APickledWalrus Date: Sat, 1 Feb 2025 14:56:44 -0500 Subject: [PATCH 13/15] Prepare For Release (2.10.1) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8207c8da4ec..028c8c3c729 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.parallel=true groupid=ch.njol name=skript -version=2.10.0 +version=2.10.1 jarName=Skript.jar testEnv=java21/paper-1.21.4 testEnvJavaVersion=21 From 90822ec8989fec07304c267d6526c69b4467b603 Mon Sep 17 00:00:00 2001 From: Eren <67760502+erenkarakal@users.noreply.github.com> Date: Wed, 5 Feb 2025 18:43:02 +0300 Subject: [PATCH 14/15] Add IntelliJ Project Icon (#7578) add icon --- .gitignore | 3 +++ .idea/icon.png | Bin 0 -> 4172 bytes 2 files changed, 3 insertions(+) create mode 100644 .idea/icon.png diff --git a/.gitignore b/.gitignore index 47df9cbc0de..5eaa3475fd6 100755 --- a/.gitignore +++ b/.gitignore @@ -166,6 +166,9 @@ fabric.properties # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 +# Exception for the icon +!.idea/icon.png + *.iml modules.xml .idea/misc.xml diff --git a/.idea/icon.png b/.idea/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ed81c8c6bae9bed6a24a922da2ae78868dd25cb3 GIT binary patch literal 4172 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 literal 0 HcmV?d00001 From 9af7c9ea93f22ecdbfe5bdcbca239694da3c1c42 Mon Sep 17 00:00:00 2001 From: burbulinis <131194155+Burbulinis@users.noreply.github.com> Date: Sun, 23 Feb 2025 20:42:35 +0200 Subject: [PATCH 15/15] String -> UUID (#7497) * init commit * optimize imports * replace try/catches with isValidUUID method, final changes * Final changes * tiny change * Tests and changes * fix test * Add a uuid function and final changes * Remove the AnyValued for UUIDs and tests for parsing uuids * Change throwing an exception to asserting false when deserializing * Clean up --------- Co-authored-by: Efnilite <35348263+Efnilite@users.noreply.github.com> --- .../skript/classes/data/BukkitClasses.java | 44 ++---- .../classes/data/DefaultComparators.java | 5 + .../classes/data/DefaultConverters.java | 5 + .../skript/classes/data/DefaultFunctions.java | 28 ++-- .../njol/skript/classes/data/JavaClasses.java | 72 +++++++++ .../njol/skript/expressions/ExprFromUUID.java | 140 ++++++++++++++++++ .../skript/expressions/ExprRandomUUID.java | 29 ++-- .../ch/njol/skript/expressions/ExprUUID.java | 65 ++++---- src/main/java/ch/njol/skript/util/Utils.java | 26 ++++ src/main/resources/lang/default.lang | 1 + .../tests/syntaxes/expressions/ExprUUID.sk | 36 +++++ 11 files changed, 363 insertions(+), 88 deletions(-) create mode 100644 src/main/java/ch/njol/skript/expressions/ExprFromUUID.java create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprUUID.sk diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index c6b17abb6d2..c547add7ab5 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -21,6 +21,7 @@ import ch.njol.skript.util.BlockUtils; import ch.njol.skript.util.PotionEffectUtils; import ch.njol.skript.util.StringMode; +import ch.njol.skript.util.Utils; import ch.njol.util.StringUtils; import ch.njol.yggdrasil.Fields; import io.papermc.paper.world.MoonPhase; @@ -71,10 +72,7 @@ public class BukkitClasses { public BukkitClasses() {} - public static final Pattern UUID_PATTERN = Pattern.compile("(?i)[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}"); - static { - final boolean GET_ENTITY_METHOD_EXISTS = Skript.methodExists(Bukkit.class, "getEntity", UUID.class); Classes.registerClass(new ClassInfo<>(Entity.class, "entity") .user("entit(y|ies)") .name("Entity") @@ -91,25 +89,10 @@ public BukkitClasses() {} .defaultExpression(new EventValueExpression<>(Entity.class)) .parser(new Parser() { @Override - @Nullable - public Entity parse(final String s, final ParseContext context) { - UUID uuid; - try { - uuid = UUID.fromString(s); - } catch (IllegalArgumentException iae) { - return null; - } - if (GET_ENTITY_METHOD_EXISTS) { - return Bukkit.getEntity(uuid); - } else { - for (World world : Bukkit.getWorlds()) { - for (Entity entity : world.getEntities()) { - if (entity.getUniqueId().equals(uuid)) { - return entity; - } - } - } - } + public @Nullable Entity parse(final String s, final ParseContext context) { + if (Utils.isValidUUID(s)) + return Bukkit.getEntity(UUID.fromString(s)); + return null; } @@ -643,8 +626,10 @@ public Player parse(String string, ParseContext context) { if (context == ParseContext.COMMAND || context == ParseContext.PARSE) { if (string.isEmpty()) return null; - if (UUID_PATTERN.matcher(string).matches()) + + if (Utils.isValidUUID(string)) return Bukkit.getPlayer(UUID.fromString(string)); + String name = string.toLowerCase(Locale.ENGLISH); int nameLength = name.length(); // caching List players = new ArrayList<>(); @@ -709,16 +694,11 @@ public String getDebugMessage(final Player p) { .after("string", "world") .parser(new Parser() { @Override - @Nullable - public OfflinePlayer parse(final String s, final ParseContext context) { - if (context == ParseContext.COMMAND || context == ParseContext.PARSE) { - if (UUID_PATTERN.matcher(s).matches()) - return Bukkit.getOfflinePlayer(UUID.fromString(s)); - else if (!SkriptConfig.playerNameRegexPattern.value().matcher(s).matches()) - return null; + public @Nullable OfflinePlayer parse(final String s, final ParseContext context) { + if (Utils.isValidUUID(s)) + return Bukkit.getOfflinePlayer(UUID.fromString(s)); + else if (SkriptConfig.playerNameRegexPattern.value().matcher(s).matches()) return Bukkit.getOfflinePlayer(s); - } - assert false; return null; } diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java b/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java index f6a2759c201..fd8f6ecb8fd 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java @@ -45,6 +45,7 @@ import org.skriptlang.skript.lang.comparator.Relation; import java.util.Objects; +import java.util.UUID; @SuppressWarnings({"rawtypes"}) public class DefaultComparators { @@ -665,6 +666,10 @@ public Relation compare(EntitySnapshot snap1, EntitySnapshot snap2) { } }); } + + // UUID + Comparators.registerComparator(UUID.class, UUID.class, (one, two) -> Relation.get(one.equals(two))); + Comparators.registerComparator(UUID.class, String.class, (one, two) -> Relation.get(one.toString().equals(two))); } } diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java b/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java index 78e5799ef92..6a7592a2706 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java @@ -39,6 +39,8 @@ import org.skriptlang.skript.lang.converter.Converters; import org.skriptlang.skript.lang.script.Script; +import java.util.UUID; + public class DefaultConverters { public DefaultConverters() {} @@ -277,6 +279,9 @@ public void setAmount(Number amount) { Converters.registerConverter(Script.class, Config.class, Script::getConfig); Converters.registerConverter(Config.class, Node.class, Config::getMainNode); + // UUID -> String + Converters.registerConverter(UUID.class, String.class, UUID::toString); + // // Entity - String (UUID) // Very slow, thus disabled for now // Converters.registerConverter(String.class, Entity.class, new Converter() { // diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java index 69a142d4827..50fc0619e13 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java @@ -11,10 +11,7 @@ import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.skript.registrations.Classes; import ch.njol.skript.registrations.DefaultClasses; -import ch.njol.skript.util.Color; -import ch.njol.skript.util.ColorRGB; -import ch.njol.skript.util.Contract; -import ch.njol.skript.util.Date; +import ch.njol.skript.util.*; import ch.njol.util.Math2; import ch.njol.util.StringUtils; import ch.njol.util.coll.CollectionUtils; @@ -544,9 +541,8 @@ public Player[] executeSimple(Object[][] params) { boolean isExact = (boolean) params[1][0]; UUID uuid = null; if (name.length() > 16 || name.contains("-")) { // shortcut - try { + if (Utils.isValidUUID(name)) uuid = UUID.fromString(name); - } catch (IllegalArgumentException ignored) {} } return CollectionUtils.array(uuid != null ? Bukkit.getPlayer(uuid) : (isExact ? Bukkit.getPlayerExact(name) : Bukkit.getPlayer(name))); } @@ -569,10 +565,8 @@ public OfflinePlayer[] executeSimple(Object[][] params) { String name = (String) params[0][0]; UUID uuid = null; if (name.length() > 16 || name.contains("-")) { // shortcut - try { + if (Utils.isValidUUID(name)) uuid = UUID.fromString(name); - } catch (IllegalArgumentException ignored) { - } } OfflinePlayer result; @@ -711,6 +705,22 @@ public String[] executeSimple(Object[][] params) { "\t\tset {_money} to formatNumber({money::%sender's uuid%})", "\t\tsend \"Your balance: %{_money}%\" to sender") .since("2.10"); + + Functions.registerFunction(new SimpleJavaFunction<>("uuid", new Parameter[]{ + new Parameter<>("uuid", DefaultClasses.STRING, true, null) + }, Classes.getExactClassInfo(UUID.class), true) { + @Override + public UUID[] executeSimple(Object[][] params) { + String uuid = (String) params[0][0]; + if (Utils.isValidUUID(uuid)) + return CollectionUtils.array(UUID.fromString(uuid)); + return new UUID[0]; + } + } + .description("Returns a UUID from the given string. The string must be in the format of a UUID.") + .examples("uuid(\"069a79f4-44e9-4726-a5be-fca90e38aaf5\")") + .since("INSERT VERSION") + ); } } diff --git a/src/main/java/ch/njol/skript/classes/data/JavaClasses.java b/src/main/java/ch/njol/skript/classes/data/JavaClasses.java index 8d9bbea8603..f9608f9aae6 100644 --- a/src/main/java/ch/njol/skript/classes/data/JavaClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/JavaClasses.java @@ -19,6 +19,8 @@ import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; +import java.io.StreamCorruptedException; +import java.util.UUID; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -325,6 +327,19 @@ public String toVariableNameString(Quaternionf quaternion) { return null; } })); + + Classes.registerClass(new ClassInfo<>(UUID.class, "uuid") + .user("uuids?") + .name("UUID") + .description( + "UUIDs are unique identifiers that ensure things can be reliably distinguished from each other. " + + "They are generated in a way that makes it practically impossible for duplicates to occur.", + "Read more about UUIDs and how they are used in Minecraft " + + "in the wiki entry about UUIDs.") + .since("INSERT VERSION") + .parser(new UUIDParser()) + .serializer(new UUIDSerializer()) + ); } /** @@ -793,4 +808,61 @@ public boolean mustSyncDeserialization() { } + private static class UUIDParser extends Parser { + + @Override + public @Nullable UUID parse(String string, ParseContext context) { + if (Utils.isValidUUID(string)) + return UUID.fromString(string); + return null; + } + + @Override + public String toString(UUID uuid, int flags) { + return uuid.toString(); + } + + @Override + public String toVariableNameString(UUID uuid) { + return uuid.toString(); + } + + } + + private static class UUIDSerializer extends Serializer { + + @Override + public Fields serialize(UUID uuid) { + Fields fields = new Fields(); + + fields.putPrimitive("mostsignificantbits", uuid.getMostSignificantBits()); + fields.putPrimitive("leastsignificantbits", uuid.getLeastSignificantBits()); + + return fields; + } + + @Override + public void deserialize(UUID o, Fields f) { + assert false; + } + + @Override + protected UUID deserialize(Fields fields) throws StreamCorruptedException { + long mostSignificantBits = fields.getAndRemovePrimitive("mostsignificantbits", long.class); + long leastSignificantBits = fields.getAndRemovePrimitive("leastsignificantbits", long.class); + return new UUID(mostSignificantBits, leastSignificantBits); + } + + @Override + public boolean mustSyncDeserialization() { + return false; + } + + @Override + protected boolean canBeInstantiated() { + return false; + } + + } + } diff --git a/src/main/java/ch/njol/skript/expressions/ExprFromUUID.java b/src/main/java/ch/njol/skript/expressions/ExprFromUUID.java new file mode 100644 index 00000000000..61de09b630a --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprFromUUID.java @@ -0,0 +1,140 @@ +package ch.njol.skript.expressions; + +import ch.njol.skript.Skript; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.SyntaxStringBuilder; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.util.Kleenean; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Name("Entity/Player/World from UUID") +@Description({ + "Get an entity, player, offline player or world from a UUID.", + "Unloaded entities or players that are offline (when using 'player from %uuid%') will return nothing." +}) +@Examples({ + "set {_player} to player from \"a0789aeb-7b46-43f6-86fb-cb671fed5775\" parsed as uuid", + "set {_offline player} to offline player from {_some uuid}", + "set {_entity} to entity from {_some uuid}", + "set {_world} to world from {_some uuid}" +}) +@Since("INSERT VERSION") +public class ExprFromUUID extends SimpleExpression { + + static { + Skript.registerExpression(ExprFromUUID.class, Object.class, ExpressionType.PROPERTY, + "[:offline[ ]]player[s] from %uuids%", + "entit(y|ies) from %uuids%", + "world[s] from %uuids%" + ); + } + + private Expression uuids; + private boolean player, offline, world; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + //noinspection unchecked + uuids = (Expression) exprs[0]; + player = matchedPattern == 0; + offline = parseResult.hasTag("offline"); + world = matchedPattern == 2; + return true; + } + + @Override + protected Object @Nullable [] get(Event event) { + List entities = new ArrayList<>(); + + for (UUID uuid : uuids.getArray(event)) { + if (player) { + if (offline) { + entities.add(Bukkit.getOfflinePlayer(uuid)); + continue; + } + + Player player = Bukkit.getPlayer(uuid); + if (player != null) + entities.add(player); + + } else if (world) { + World world = Bukkit.getWorld(uuid); + if (world != null) + entities.add(world); + + } else { + Entity entity = Bukkit.getEntity(uuid); + if (entity != null) + entities.add(entity); + } + } + + if (player) { + if (offline) + //noinspection SuspiciousToArrayCall + return entities.toArray(OfflinePlayer[]::new); + //noinspection SuspiciousToArrayCall + return entities.toArray(Player[]::new); + } + + if (world) + //noinspection SuspiciousToArrayCall + return entities.toArray(World[]::new); + //noinspection SuspiciousToArrayCall + return entities.toArray(Entity[]::new); + } + + @Override + public boolean isSingle() { + return uuids.isSingle(); + } + + @Override + public Class getReturnType() { + if (world) { + return World.class; + } else if (player) { + if (offline) + return OfflinePlayer.class; + return Player.class; + } + + return Entity.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug); + + if (world) { + builder.append("worlds"); + } else if (player) { + if (offline) + builder.append("offline"); + builder.append("players"); + } else { + builder.append("entities"); + } + + builder.append("from", uuids); + + return builder.toString(); + } + +} diff --git a/src/main/java/ch/njol/skript/expressions/ExprRandomUUID.java b/src/main/java/ch/njol/skript/expressions/ExprRandomUUID.java index 8a75a30e5ad..eebb120ef75 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprRandomUUID.java +++ b/src/main/java/ch/njol/skript/expressions/ExprRandomUUID.java @@ -19,37 +19,36 @@ @Name("Random UUID") @Description("Returns a random UUID.") @Examples("set {_uuid} to random uuid") -@Since("2.5.1") -public class ExprRandomUUID extends SimpleExpression { - +@Since("2.5.1, INSERT VERSION (return UUIDs)") +public class ExprRandomUUID extends SimpleExpression { + static { - Skript.registerExpression(ExprRandomUUID.class, String.class, ExpressionType.SIMPLE, "[a] random uuid"); + Skript.registerExpression(ExprRandomUUID.class, UUID.class, ExpressionType.SIMPLE, "[a] random uuid"); } - + @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { return true; } - + @Override - @Nullable - protected String[] get(Event e) { - return new String[] {UUID.randomUUID().toString()}; + protected UUID @Nullable [] get(Event e) { + return new UUID[]{ UUID.randomUUID() }; } - + @Override public boolean isSingle() { return true; } - + @Override - public Class getReturnType() { - return String.class; + public Class getReturnType() { + return UUID.class; } - + @Override public String toString(@Nullable Event e, boolean debug) { return "random uuid"; } - + } diff --git a/src/main/java/ch/njol/skript/expressions/ExprUUID.java b/src/main/java/ch/njol/skript/expressions/ExprUUID.java index 4f0a0022a26..8419dc10aa9 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprUUID.java +++ b/src/main/java/ch/njol/skript/expressions/ExprUUID.java @@ -3,7 +3,6 @@ import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; import ch.njol.skript.Skript; @@ -13,35 +12,37 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -/** - * @author Peter Güttinger - */ +import java.util.UUID; + @Name("UUID") -@Description({"The UUID of a player, entity or world.", - "In the future there will be an option to use a player's UUID instead of the name in variable names (i.e. when %player% is used), but for now this can be used.", - "Please note that this expression does not work for offline players if you are under 1.8!"}) -// TODO [UUID] update documentation after release. Add note about requiring Bukkit 1.7.(9/10)? -@Examples({"# prevents people from joining the server if they use the name of a player", - "# who has played on this server at least once since this script has been added", - "on login:", - " if {uuid::%name of player%} exists:", - " {uuid::%name of player%} is not uuid of player", - " kick player due to \"Someone with your name has played on this server before\"", - " else:", - " set {uuid::%name of player%} to uuid of player"}) -@Since("2.1.2, 2.2 (offline players' UUIDs), 2.2-dev24 (other entities' UUIDs)") -public class ExprUUID extends SimplePropertyExpression { +@Description("The UUID of a player, entity or world.") +@Examples({ + "# prevents people from joining the server if they use the name of a player", + "# who has played on this server at least once since this script has been added", + "on login:", + "\tif {uuid::%name of player%} exists:", + "\t\t{uuid::%name of player%} is not uuid of player", + "\t\tkick player due to \"Someone with your name has played on this server before\"", + "\telse:", + "\t\tset {uuid::%name of player%} to uuid of player", + "", + "command /what-is-my-uuid:", + "\ttrigger:", + "\t\tset {_uuid} to uuid of player", + "\t\tsend \"Your UUID is '%string within {_uuid}%'\"", +}) +@Since("2.1.2, 2.2 (offline players' uuids), 2.2-dev24 (other entities' uuids)") +public class ExprUUID extends SimplePropertyExpression { static { - register(ExprUUID.class, String.class, "UUID", "offlineplayers/worlds/entities"); + register(ExprUUID.class, UUID.class, "UUID", "offlineplayers/worlds/entities"); } @Override - @Nullable - public String convert(final Object o) { - if (o instanceof OfflinePlayer) { + public @Nullable UUID convert(Object object) { + if (object instanceof OfflinePlayer player) { try { - return ((OfflinePlayer) o).getUniqueId().toString(); + return player.getUniqueId(); } catch (UnsupportedOperationException e) { // Some plugins (ProtocolLib) try to emulate offline players, but fail miserably // They will throw this exception... and somehow server may freeze when this happens @@ -49,22 +50,22 @@ public String convert(final Object o) { e.printStackTrace(); return null; } - } else if (o instanceof Entity) { - return ((Entity)o).getUniqueId().toString(); - } else if (o instanceof World) { - return ((World) o).getUID().toString(); + } else if (object instanceof Entity entity) { + return entity.getUniqueId(); + } else if (object instanceof World world) { + return world.getUID(); } return null; } - + @Override - public Class getReturnType() { - return String.class; + public Class getReturnType() { + return UUID.class; } - + @Override protected String getPropertyName() { return "UUID"; } - + } diff --git a/src/main/java/ch/njol/skript/util/Utils.java b/src/main/java/ch/njol/skript/util/Utils.java index 100c488cb95..362ea97e70f 100644 --- a/src/main/java/ch/njol/skript/util/Utils.java +++ b/src/main/java/ch/njol/skript/util/Utils.java @@ -903,4 +903,30 @@ If there are no supported versions, you should contact the author(s): %s, and as return 0; } + /** + * Checks if the provided string is a valid {@link UUID}. + * @param uuid the string + * @return whether the given string is a valid UUID + */ + public static boolean isValidUUID(String uuid) { + if (uuid == null || uuid.length() != 36) + return false; + + if (uuid.charAt(8) != '-' || uuid.charAt(13) != '-' || uuid.charAt(18) != '-' || uuid.charAt(23) != '-') { + return false; + } + + for (int i = 0; i < 36; i++) { + if (i == 8 || i == 13 || i == 18 || i == 23) + continue; + + char c = uuid.charAt(i); + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + return false; + } + } + + return true; + } + } diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index e380aef7bcf..874ec3b2263 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -2586,6 +2586,7 @@ types: boolean: boolean¦ (yes/no)¦s (yes/no) @a string: text¦s @a chunk: chunk¦s @a + uuid: uuid¦s @a # Bukkit entity: entit¦y¦ies @an diff --git a/src/test/skript/tests/syntaxes/expressions/ExprUUID.sk b/src/test/skript/tests/syntaxes/expressions/ExprUUID.sk new file mode 100644 index 00000000000..eac3cbaf1f5 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprUUID.sk @@ -0,0 +1,36 @@ +test "uuid": + set {_lookup} to offline player from ("a0789aeb-7b46-43f6-86fb-cb671fed5775" parsed as uuid) + assert {_lookup} is set with "Failed to look up offline player from uuid" + + set {_uuid} to ("a0789aeb-7b46-43f6-86fb-cb671fed5775" parsed as uuid) + assert {_uuid} is "a0789aeb-7b46-43f6-86fb-cb671fed5775" with "failed to compare uuid and string" + assert "a0789aeb-7b46-43f6-86fb-cb671fed5775" is {_uuid} with "failed to compare string and uuid" + + spawn a pig at test-location + set {_pig} to last spawned pig + + set {_uuid} to uuid of {_pig} + + set {_lookup} to entity from {_uuid} + assert {_lookup} is {_pig} with "Failed to look up pig" + + set {_le pig} to {_uuid} parsed as entity + assert {_le pig} is {_pig} with "failed to parse uuid as entity" + + delete entity within {_pig} + + set {_lookup} to entity from ("blah" parsed as uuid) + assert {_lookup} is set to fail with "entity is set without a valid uuid" + + # because this player is not online, should fail + set {_lookup} to player from ("a0789aeb-7b46-43f6-86fb-cb671fed5775" parsed as uuid) + assert {_lookup} is "Burbulinis" to fail with "player is set without being online" + + set {_lookup} to "Njol" parsed as offline player + assert {_lookup} is "Njol" with "failed to look up offline player from name" + + set {_uuid} to uuid of world of test-location + assert {_uuid} is set with "Failed to get world uuid" + + set {_world} to world from {_uuid} + assert {_world} is set with "Failed to get world from uuid"