denizenType, DataComponentType.Valued componentType, String name) {
+ super(componentType, denizenType, name);
+ }
+
+ public abstract D toDenizen(P value);
+
+ public abstract P fromDenizen(D value, Mechanism mechanism);
+
+ @Override
+ public D getValue(ItemStack item) {
+ P data = item.getData(componentType);
+ return data != null ? toDenizen(data) : null;
+ }
+
+ @Override
+ public void setValue(ItemStack item, D value, Mechanism mechanism) {
+ P converted = fromDenizen(value, mechanism);
+ if (converted != null) {
+ item.setData(componentType, converted);
+ }
+ }
+ }
+
+ public class Property extends ItemProperty {
+
+ private static DataComponentAdapter, ?> currentlyRegisteringComponentAdapter;
+
+ public Property(ItemTag item) {
+ this.object = item;
+ }
+
+ @Override
+ public D getPropertyValue() {
+ return getValue(getItemStack());
+ }
+
+ @Override
+ public D getPropertyValueNoDefault() {
+ if (!getItemStack().isDataOverridden(componentType)) {
+ return null;
+ }
+ return super.getPropertyValueNoDefault();
+ }
+
+ @Override
+ public boolean isDefaultValue(D value) {
+ return DataComponentAdapter.this.isDefaultValue(value);
+ }
+
+ @Override
+ public void setPropertyValue(D value, Mechanism mechanism) {
+ if (value == null) {
+ getItemStack().resetData(componentType);
+ return;
+ }
+ setValue(getItemStack(), value, mechanism);
+ }
+
+ @Override
+ public String getPropertyId() {
+ return name;
+ }
+
+ public static void register() {
+ autoRegisterNullable(currentlyRegisteringComponentAdapter.name, DataComponentAdapter.Property.class, currentlyRegisteringComponentAdapter.denizenType, false);
+ }
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java
new file mode 100644
index 0000000000..4f6710f1fd
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java
@@ -0,0 +1,46 @@
+package com.denizenscript.denizen.paper.datacomponents;
+
+import com.denizenscript.denizencore.objects.Mechanism;
+import com.denizenscript.denizencore.objects.core.ElementTag;
+import com.denizenscript.denizencore.objects.core.MapTag;
+import io.papermc.paper.datacomponent.DataComponentTypes;
+import io.papermc.paper.datacomponent.item.FoodProperties;
+
+public class FoodAdapter extends DataComponentAdapter.Valued {
+
+ // <--[property]
+ // @object ItemTag
+ // @name food
+ // @input MapTag
+ // @description
+ // Controls an item's food <@link language Item Components>.
+ // The map includes keys:
+ // - "nutrition", ElementTag(Number) representing the amount of food points restored by this item.
+ // - "saturation", ElementTag(Decimal) representing the amount of saturation points restored by this item.
+ // - "can_always_eat", ElementTag(Boolean) controlling whether the item can always be eaten, even if the player isn't hungry.
+ // @mechanism
+ // Provide no input to reset the item to its default value.
+ // -->
+
+ public FoodAdapter() {
+ super(MapTag.class, DataComponentTypes.FOOD, "food");
+ }
+
+ @Override
+ public MapTag toDenizen(FoodProperties value) {
+ MapTag foodData = new MapTag();
+ foodData.putObject("nutrition", new ElementTag(value.nutrition()));
+ foodData.putObject("saturation", new ElementTag(value.saturation()));
+ foodData.putObject("can_always_eat", new ElementTag(value.canAlwaysEat()));
+ return foodData;
+ }
+
+ @Override
+ public FoodProperties fromDenizen(MapTag value, Mechanism mechanism) {
+ FoodProperties.Builder builder = FoodProperties.food();
+ setIfValid(builder::nutrition, value, "nutrition", ElementTag.class, ElementTag::isInt, ElementTag::asInt, "number", mechanism);
+ setIfValid(builder::saturation, value, "saturation", ElementTag.class, ElementTag::isFloat, ElementTag::asFloat, "decimal number", mechanism);
+ setIfValid(builder::canAlwaysEat, value, "can_always_eat", ElementTag.class, ElementTag::isBoolean, ElementTag::asBoolean, "boolean", mechanism);
+ return builder.build();
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/GliderAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/GliderAdapter.java
new file mode 100644
index 0000000000..838f02ca72
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/GliderAdapter.java
@@ -0,0 +1,20 @@
+package com.denizenscript.denizen.paper.datacomponents;
+
+import io.papermc.paper.datacomponent.DataComponentTypes;
+
+public class GliderAdapter extends DataComponentAdapter.NonValued {
+
+ // <--[property]
+ // @object ItemTag
+ // @name glider
+ // @input ElementTag(Boolean)
+ // @description
+ // Controls whether an item can be used to glide when equipped (like elytras by default), see <@link language Item Components>.
+ // @mechanism
+ // Provide no input to reset the item to its default value.
+ // -->
+
+ public GliderAdapter() {
+ super(DataComponentTypes.GLIDER, "glider");
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/AnvilBlockDamagedScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/AnvilBlockDamagedScriptEvent.java
index 79af137dc8..effc8f9e27 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/AnvilBlockDamagedScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/AnvilBlockDamagedScriptEvent.java
@@ -3,10 +3,10 @@
import com.denizenscript.denizen.events.BukkitScriptEvent;
import com.denizenscript.denizen.objects.InventoryTag;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
+import com.denizenscript.denizen.utilities.inventory.InventoryViewUtil;
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
-import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.destroystokyo.paper.event.block.AnvilDamagedEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -42,6 +42,20 @@ public class AnvilBlockDamagedScriptEvent extends BukkitScriptEvent implements L
public AnvilBlockDamagedScriptEvent() {
registerCouldMatcher("anvil block damaged|breaks");
registerSwitches("state");
+ this.registerOptionalDetermination("state", ElementTag.class, (evt, context, state) -> {
+ if (state.matchesEnum(AnvilDamagedEvent.DamageState.class)) {
+ evt.event.setDamageState(state.asEnum(AnvilDamagedEvent.DamageState.class));
+ return true;
+ }
+ return false;
+ });
+ this.registerOptionalDetermination("break", ElementTag.class, (evt, context, value) -> {
+ if (value.isBoolean()) {
+ evt.event.setBreaking(value.asBoolean());
+ return true;
+ }
+ return false;
+ });
}
public AnvilDamagedEvent event;
@@ -62,36 +76,17 @@ public boolean matches(ScriptPath path) {
@Override
public ObjectTag getContext(String name) {
- switch (name) {
- case "state": return new ElementTag(event.getDamageState());
- case "inventory": return InventoryTag.mirrorBukkitInventory(event.getInventory());
- case "break": return new ElementTag(event.isBreaking());
- }
- return super.getContext(name);
- }
-
- @Override
- public boolean applyDetermination(ScriptPath path, ObjectTag determinationObj) {
- if (determinationObj instanceof ElementTag) {
- String lower = CoreUtilities.toLowerCase(determinationObj.toString());
- if (lower.startsWith("state:")) {
- ElementTag stateElement = new ElementTag(lower.substring("state:".length()));
- if (stateElement.matchesEnum(AnvilDamagedEvent.DamageState.class)) {
- event.setDamageState(stateElement.asEnum(AnvilDamagedEvent.DamageState.class));
- return true;
- }
- }
- else if (lower.startsWith("break:")) {
- event.setBreaking(new ElementTag(lower.substring("break:".length())).asBoolean());
- return true;
- }
- }
- return super.applyDetermination(path, determinationObj);
+ return switch (name) {
+ case "state" -> new ElementTag(event.getDamageState());
+ case "inventory" -> InventoryTag.mirrorBukkitInventory(event.getInventory());
+ case "break" -> new ElementTag(event.isBreaking());
+ default -> super.getContext(name);
+ };
}
@Override
public ScriptEntryData getScriptEntryData() {
- return new BukkitScriptEntryData(event.getView().getPlayer());
+ return new BukkitScriptEntryData(InventoryViewUtil.getPlayer(event.getView()));
}
@EventHandler
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/BellRingScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/BellRingScriptEvent.java
index b70112effa..40ef73ef82 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/BellRingScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/BellRingScriptEvent.java
@@ -12,27 +12,6 @@
public class BellRingScriptEvent extends BukkitScriptEvent implements Listener {
- // <--[event]
- // @Events
- // bell rings
- //
- // @Location true
- //
- // @Plugin Paper
- //
- // @Group Paper
- //
- // @Cancellable true
- //
- // @Triggers when a bell block rings.
- //
- // @Context
- // returns the entity that rung the bell, if any.
- // returns the location of the bell being rung.
- //
- // @Player when the ringing entity is a player.
- //
- // -->
public BellRingScriptEvent() {
registerCouldMatcher("bell rings");
@@ -58,7 +37,7 @@ public ScriptEntryData getScriptEntryData() {
public ObjectTag getContext(String name) {
switch (name) {
case "entity":
- return event.getEntity() == null ? null : new EntityTag(event.getEntity());
+ return event.getEntity() == null ? null : new EntityTag(event.getEntity()).getDenizenObject();
case "location": return location;
}
return super.getContext(name);
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/DragonEggFormScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/DragonEggFormScriptEvent.java
new file mode 100644
index 0000000000..944edf8062
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/DragonEggFormScriptEvent.java
@@ -0,0 +1,77 @@
+package com.denizenscript.denizen.paper.events;
+
+import com.denizenscript.denizen.events.BukkitScriptEvent;
+import com.denizenscript.denizen.objects.EntityTag;
+import com.denizenscript.denizen.objects.LocationTag;
+import com.denizenscript.denizencore.objects.ObjectTag;
+import com.denizenscript.denizencore.objects.core.ElementTag;
+import com.denizenscript.denizencore.objects.core.ListTag;
+import io.papermc.paper.event.block.DragonEggFormEvent;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+public class DragonEggFormScriptEvent extends BukkitScriptEvent implements Listener {
+
+ // <--[event]
+ // @Events
+ // dragon egg forms
+ //
+ // @Plugin Paper
+ //
+ // @Group Paper
+ //
+ // @Location true
+ //
+ // @Triggers when the ender dragon is defeated and the dragon egg forms.
+ //
+ // @Context
+ // returns the EntityTag of the ender dragon right before it's removed.
+ // returns the LocationTag of the dragon egg.
+ // returns the LocationTag of the end portal.
+ // returns an ElementTag(Boolean) of whether the dragon has been previously killed.
+ // returns an ElementTag of the respawn phase. Valid values can be found at <@link url https://jd.papermc.io/paper/1.21.3/org/bukkit/boss/DragonBattle.RespawnPhase.html>.
+ // returns a ListTag(EntityTag) of the healing end crystals.
+ // returns a ListTag(EntityTag) of the respawn end crystals.
+ //
+ // -->
+
+ public DragonEggFormScriptEvent() {
+ registerCouldMatcher("dragon egg forms");
+ }
+
+ public LocationTag location;
+ public EntityTag entity;
+ public DragonEggFormEvent event;
+
+ @Override
+ public boolean matches(ScriptPath path) {
+ if (!runInCheck(path, location)) {
+ return false;
+ }
+ return super.matches(path);
+ }
+
+ @Override
+ public ObjectTag getContext(String name) {
+ return switch (name) {
+ case "entity" -> entity;
+ case "location" -> location;
+ case "end_portal_location" -> new LocationTag(event.getDragonBattle().getEndPortalLocation());
+ case "previously_killed" -> new ElementTag(event.getDragonBattle().hasBeenPreviouslyKilled());
+ case "respawn_phase" -> new ElementTag(event.getDragonBattle().getRespawnPhase());
+ case "healing_crystals" -> new ListTag(event.getDragonBattle().getHealingCrystals(), EntityTag::new);
+ case "respawn_crystals" -> new ListTag(event.getDragonBattle().getRespawnCrystals(), EntityTag::new);
+ default -> super.getContext(name);
+ };
+ }
+
+ @EventHandler
+ public void onDragonEggForms(DragonEggFormEvent event) {
+ location = new LocationTag(event.getBlock().getLocation());
+ entity = new EntityTag(event.getDragonBattle().getEnderDragon());
+ this.event = event;
+ EntityTag.rememberEntity(entity.getBukkitEntity());
+ fire(event);
+ EntityTag.forgetEntity(entity.getBukkitEntity());
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/EntityKnocksbackEntityScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/EntityKnocksbackEntityScriptEvent.java
index 713e8133f1..09ec78f7c8 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/EntityKnocksbackEntityScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/EntityKnocksbackEntityScriptEvent.java
@@ -2,10 +2,11 @@
import com.denizenscript.denizen.events.BukkitScriptEvent;
import com.denizenscript.denizen.objects.EntityTag;
+import com.denizenscript.denizen.objects.ItemTag;
import com.denizenscript.denizen.objects.LocationTag;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
+import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.objects.ObjectTag;
-import com.denizenscript.denizen.objects.ItemTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
import com.destroystokyo.paper.event.entity.EntityKnockbackByEntityEvent;
import org.bukkit.event.EventHandler;
@@ -19,6 +20,8 @@ public class EntityKnocksbackEntityScriptEvent extends BukkitScriptEvent impleme
//
// @Location true
//
+ // @Warning this event may in some cases double-fire, requiring usage of the 'ratelimit' command (like 'ratelimit 1t') to prevent doubling actions.
+ //
// @Switch with:- to only process the event when the item used to cause damage (in the damager's hand) is a specified item.
//
// @Plugin Paper
@@ -33,6 +36,7 @@ public class EntityKnocksbackEntityScriptEvent extends BukkitScriptEvent impleme
// returns the EntityTag that was knocked back.
// returns the EntityTag of the one who knocked.
// returns the knockback applied as a vector.
+ // returns the cause of the knockback (only on MC 1.20+). Causes list: <@link url https://jd.papermc.io/paper/1.21.1/io/papermc/paper/event/entity/EntityKnockbackEvent.Cause.html>
//
// @Determine
// LocationTag as a vector to change the acceleration applied.
@@ -58,7 +62,7 @@ public EntityKnocksbackEntityScriptEvent() {
public boolean matches(ScriptPath path) {
String attacker = path.eventArgLowerAt(0);
String target = path.eventArgLowerAt(3);
- if (!hitBy.tryAdvancedMatcher(attacker) || (!entity.tryAdvancedMatcher(target))) {
+ if (!hitBy.tryAdvancedMatcher(attacker, path.context) || (!entity.tryAdvancedMatcher(target, path.context))) {
return false;
}
if (!runInCheck(path, entity.getLocation())) {
@@ -88,15 +92,13 @@ public ScriptEntryData getScriptEntryData() {
@Override
public ObjectTag getContext(String name) {
- switch (name) {
- case "entity":
- return entity.getDenizenObject();
- case "damager":
- return hitBy.getDenizenObject();
- case "acceleration":
- return new LocationTag(event.getAcceleration());
- }
- return super.getContext(name);
+ return switch (name) {
+ case "entity" -> entity.getDenizenObject();
+ case "damager" -> hitBy.getDenizenObject();
+ case "acceleration" -> new LocationTag(event.getAcceleration());
+ case "cause" -> new ElementTag(event.getCause().name(), true); // TODO: once 1.20 is the minimum supported version, use the enum constructor
+ default -> super.getContext(name);
+ };
}
@EventHandler
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/EntityLoadCrossbowScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/EntityLoadCrossbowScriptEvent.java
index 34fadbc0b0..77eb0a94eb 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/EntityLoadCrossbowScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/EntityLoadCrossbowScriptEvent.java
@@ -7,7 +7,6 @@
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
-import com.denizenscript.denizencore.utilities.CoreUtilities;
import io.papermc.paper.event.entity.EntityLoadCrossbowEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -48,9 +47,11 @@ public class EntityLoadCrossbowScriptEvent extends BukkitScriptEvent implements
public EntityLoadCrossbowScriptEvent() {
registerCouldMatcher(" loads crossbow");
registerSwitches("crossbow");
+ this.registerTextDetermination("keep_item", (evt) -> {
+ evt.event.setConsumeItem(false);
+ });
}
-
public EntityLoadCrossbowEvent event;
public EntityTag entity;
@@ -70,31 +71,15 @@ public ScriptEntryData getScriptEntryData() {
return new BukkitScriptEntryData(entity);
}
- @Override
- public boolean applyDetermination(ScriptPath path, ObjectTag determinationObj) {
- if (determinationObj instanceof ElementTag) {
- String lower = CoreUtilities.toLowerCase(determinationObj.toString());
- if (lower.equals("keep_item")) {
- event.setConsumeItem(false);
- return true;
- }
- }
- return super.applyDetermination(path, determinationObj);
- }
-
@Override
public ObjectTag getContext(String name) {
- switch (name) {
- case "entity":
- return entity.getDenizenObject();
- case "crossbow":
- return new ItemTag(event.getCrossbow());
- case "hand":
- return new ElementTag(event.getHand());
- case "consumes":
- return new ElementTag(event.shouldConsumeItem());
- }
- return super.getContext(name);
+ return switch (name) {
+ case "entity" -> entity.getDenizenObject();
+ case "crossbow" -> new ItemTag(event.getCrossbow());
+ case "hand" -> new ElementTag(event.getHand());
+ case "consumes" -> new ElementTag(event.shouldConsumeItem());
+ default -> super.getContext(name);
+ };
}
@EventHandler
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerBeaconEffectScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerBeaconEffectScriptEvent.java
index 6f204e60e6..99baed8d42 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerBeaconEffectScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerBeaconEffectScriptEvent.java
@@ -31,7 +31,7 @@ public class PlayerBeaconEffectScriptEvent extends BukkitScriptEvent implements
//
// @Context
// returns the LocationTag of the beacon applying an effect.
- // returns a MapTag of the potion effect (in the same format as <@link tag EntityTag.effects_data>).
+ // returns a MapTag of the potion effect in <@link language Potion Effect Format>.
// returns an ElementTag of the effect type.
// returns an ElementTag(Boolean) of whether the beacon effect is the primary effect.
//
@@ -78,7 +78,7 @@ public ObjectTag getContext(String name) {
return switch (name) {
case "location" -> new LocationTag(event.getBlock().getLocation());
case "effect" -> new ElementTag(ItemPotion.effectToLegacyString(event.getEffect(), null));
- case "effect_data" -> ItemPotion.effectToMap(event.getEffect());
+ case "effect_data" -> ItemPotion.effectToMap(event.getEffect(), true);
case "effect_type" -> new ElementTag(event.getEffect().getType().getName());
case "is_primary" -> new ElementTag(event.isPrimary());
default -> super.getContext(name);
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerChangesFramedItemScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerChangesFramedItemScriptEvent.java
new file mode 100644
index 0000000000..76745156e5
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerChangesFramedItemScriptEvent.java
@@ -0,0 +1,97 @@
+package com.denizenscript.denizen.paper.events;
+
+import com.denizenscript.denizen.events.BukkitScriptEvent;
+import com.denizenscript.denizen.objects.EntityTag;
+import com.denizenscript.denizen.objects.ItemTag;
+import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
+import com.denizenscript.denizencore.objects.ObjectTag;
+import com.denizenscript.denizencore.objects.core.ElementTag;
+import com.denizenscript.denizencore.scripts.ScriptEntryData;
+import io.papermc.paper.event.player.PlayerItemFrameChangeEvent;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+public class PlayerChangesFramedItemScriptEvent extends BukkitScriptEvent implements Listener {
+
+ // <--[event]
+ // @Events
+ // player changes framed
-
+ //
+ // @Location true
+ //
+ // @Plugin Paper
+ //
+ // @Group Paper
+ //
+ // @Cancellable true
+ //
+ // @Triggers when a player interacts with an item frame by adding, removing, or rotating the item held in it.
+ //
+ // @Switch frame: to only process the event if the item frame entity being matches the input.
+ // @Switch action: to only process the event if the change matches the input.
+ //
+ // @Context
+ // returns the EntityTag of the item frame.
+ // returns the ItemTag of the item held in the item frame.
+ // returns the ElementTag of the action being performed, based on <@link url https://jd.papermc.io/paper/1.20/io/papermc/paper/event/player/PlayerItemFrameChangeEvent.ItemFrameChangeAction.html>
+ //
+ // @Determine
+ // "ITEM:" to change the item held by the item frame. If there is an item already in the frame, it will be replaced. To remove the item, set it to air.
+ //
+ // @Player Always.
+ // -->
+
+ public PlayerChangesFramedItemScriptEvent() {
+ registerCouldMatcher("player changes framed
- ");
+ registerSwitches("frame", "action");
+ this.registerDetermination("item", ItemTag.class, (evt, context, item) -> {
+ evt.event.setItemStack(item.getItemStack());
+ });
+ }
+
+ public ItemTag item;
+ public EntityTag frame;
+ public ElementTag action;
+ public PlayerItemFrameChangeEvent event;
+
+ @Override
+ public boolean matches(ScriptPath path) {
+ if (!path.tryArgObject(3, item)) {
+ return false;
+ }
+ if (!path.tryObjectSwitch("frame", frame)) {
+ return false;
+ }
+ if (!path.tryObjectSwitch("action", action)) {
+ return false;
+ }
+ if (!runInCheck(path, frame.getLocation())) {
+ return false;
+ }
+ return super.matches(path);
+ }
+
+ @Override
+ public ObjectTag getContext(String name) {
+ return switch (name) {
+ case "frame" -> frame;
+ case "item" -> new ItemTag(event.getItemStack());
+ case "action" -> action;
+ default -> super.getContext(name);
+ };
+ }
+
+ @Override
+ public ScriptEntryData getScriptEntryData() {
+ return new BukkitScriptEntryData(event.getPlayer());
+ }
+
+ @EventHandler
+ public void onPlayerChangesFramedItem(PlayerItemFrameChangeEvent event) {
+ item = new ItemTag(event.getItemStack());
+ frame = new EntityTag(event.getItemFrame());
+ action = new ElementTag(event.getAction());
+ this.event = event;
+ fire(event);
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerChunkUnloadScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerChunkUnloadScriptEvent.java
new file mode 100644
index 0000000000..3199313aa8
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerChunkUnloadScriptEvent.java
@@ -0,0 +1,68 @@
+package com.denizenscript.denizen.paper.events;
+
+import com.denizenscript.denizen.events.BukkitScriptEvent;
+import com.denizenscript.denizen.objects.ChunkTag;
+import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
+import com.denizenscript.denizencore.objects.ObjectTag;
+import com.denizenscript.denizencore.scripts.ScriptEntryData;
+import io.papermc.paper.event.packet.PlayerChunkUnloadEvent;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+public class PlayerChunkUnloadScriptEvent extends BukkitScriptEvent implements Listener {
+
+ // <--[event]
+ // @Events
+ // player receives chunk unload
+ //
+ // @Group Paper
+ //
+ // @Location true
+ //
+ // @Plugin Paper
+ //
+ // @Warning This event will fire *extremely* rapidly and almost guarantees lag. Use with maximum caution.
+ //
+ // @Triggers when a Player receives a chunk unload packet.
+ // Should only be used for packet/clientside related stuff. Not intended for modifying server side.
+ // Generally prefer <@link event chunk unloads> in most cases.
+ //
+ // @Context
+ // returns a ChunkTag of the chunk being unloaded.
+ //
+ // @Player Always.
+ // -->
+
+ public PlayerChunkUnloadScriptEvent() {
+ registerCouldMatcher("player receives chunk unload");
+ }
+
+ public PlayerChunkUnloadEvent event;
+
+ @Override
+ public boolean matches(ScriptPath path) {
+ if (!runInCheck(path, event.getPlayer().getLocation())) {
+ return false;
+ }
+ return super.matches(path);
+ }
+
+ @Override
+ public ScriptEntryData getScriptEntryData() {
+ return new BukkitScriptEntryData(event.getPlayer());
+ }
+
+ @Override
+ public ObjectTag getContext(String name) {
+ return switch (name) {
+ case "chunk" -> new ChunkTag(event.getChunk());
+ default -> super.getContext(name);
+ };
+ }
+
+ @EventHandler
+ public void playerChunkUnloadEvent(PlayerChunkUnloadEvent event) {
+ this.event = event;
+ fire(event);
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerElytraBoostScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerElytraBoostScriptEvent.java
index 9063369f10..2a8300cc43 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerElytraBoostScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerElytraBoostScriptEvent.java
@@ -8,7 +8,6 @@
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
-import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.destroystokyo.paper.event.player.PlayerElytraBoostEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -47,6 +46,13 @@ public class PlayerElytraBoostScriptEvent extends BukkitScriptEvent implements L
public PlayerElytraBoostScriptEvent() {
registerCouldMatcher("player boosts elytra");
registerSwitches("with", "elytra");
+ this.registerOptionalDetermination("keep", ElementTag.class, (evt, context, value) -> {
+ if (value.isBoolean()) {
+ evt.event.setShouldConsume(!value.asBoolean());
+ return true;
+ }
+ return false;
+ });
}
public PlayerElytraBoostEvent event;
@@ -74,26 +80,12 @@ public ScriptEntryData getScriptEntryData() {
@Override
public ObjectTag getContext(String name) {
- switch (name) {
- case "item":
- return firework;
- case "entity":
- return new EntityTag(event.getFirework());
- case "should_keep":
- return new ElementTag(!event.shouldConsume());
- }
- return super.getContext(name);
- }
-
- @Override
- public boolean applyDetermination(ScriptPath path, ObjectTag determinationObj) {
- String determination = CoreUtilities.toLowerCase(determinationObj.toString());
- if (determination.startsWith("keep:")) {
- String value = determination.substring("keep:".length());
- event.setShouldConsume(!(new ElementTag(value).asBoolean()));
- return true;
- }
- return super.applyDetermination(path, determinationObj);
+ return switch (name) {
+ case "item" -> firework;
+ case "entity" -> new EntityTag(event.getFirework());
+ case "should_keep" -> new ElementTag(!event.shouldConsume());
+ default -> super.getContext(name);
+ };
}
@EventHandler
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerEquipsArmorScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerEquipsArmorScriptEvent.java
index 204f0587c8..2c3b933852 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerEquipsArmorScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerEquipsArmorScriptEvent.java
@@ -77,7 +77,7 @@ public boolean matches(ScriptPath path) {
return false;
}
}
- else if (!itemCompare.equals("armor") && !newItem.tryAdvancedMatcher(itemCompare)) {
+ else if (!itemCompare.equals("armor") && !newItem.tryAdvancedMatcher(itemCompare, path.context)) {
return false;
}
}
@@ -87,7 +87,7 @@ else if (!itemCompare.equals("armor") && !newItem.tryAdvancedMatcher(itemCompare
return false;
}
}
- else if (!itemCompare.equals("armor") && !oldItem.tryAdvancedMatcher(itemCompare)) {
+ else if (!itemCompare.equals("armor") && !oldItem.tryAdvancedMatcher(itemCompare, path.context)) {
return false;
}
}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerLecternPageChangeScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerLecternPageChangeScriptEvent.java
index 7ffe51f311..c4a7a555a1 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerLecternPageChangeScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerLecternPageChangeScriptEvent.java
@@ -7,7 +7,6 @@
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
-import com.denizenscript.denizencore.utilities.CoreUtilities;
import io.papermc.paper.event.player.PlayerLecternPageChangeEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -57,6 +56,13 @@ public class PlayerLecternPageChangeScriptEvent extends BukkitScriptEvent implem
public PlayerLecternPageChangeScriptEvent() {
registerCouldMatcher("player flips lectern page");
registerSwitches("book");
+ this.registerOptionalDetermination("page", ElementTag.class, (evt, context, page) -> {
+ if (page.isInt()) {
+ evt.event.setNewPage(page.asInt() - 1);
+ return true;
+ }
+ return false;
+ });
}
public PlayerLecternPageChangeEvent event;
@@ -88,21 +94,6 @@ public ObjectTag getContext(String name) {
default -> super.getContext(name);
};
}
-
- @Override
- public boolean applyDetermination(ScriptPath path, ObjectTag determinationObj) {
- if (determinationObj instanceof ElementTag) {
- String lower = CoreUtilities.toLowerCase(determinationObj.toString());
- if (lower.startsWith("page:")) {
- ElementTag value = new ElementTag(lower.substring("page:".length()));
- if (value.isInt()) {
- event.setNewPage(value.asInt() - 1);
- return true;
- }
- }
- }
- return super.applyDetermination(path, determinationObj);
- }
@EventHandler
public void onPlayerFlipsLecternPage(PlayerLecternPageChangeEvent event) {
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerLoomPatternSelectScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerLoomPatternSelectScriptEvent.java
index abfdb486f5..ca27f1747f 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerLoomPatternSelectScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerLoomPatternSelectScriptEvent.java
@@ -2,11 +2,11 @@
import com.denizenscript.denizen.events.BukkitScriptEvent;
import com.denizenscript.denizen.objects.InventoryTag;
+import com.denizenscript.denizen.utilities.Utilities;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
-import com.denizenscript.denizencore.utilities.CoreUtilities;
import io.papermc.paper.event.player.PlayerLoomPatternSelectEvent;
import org.bukkit.block.banner.PatternType;
import org.bukkit.event.EventHandler;
@@ -55,6 +55,13 @@ public class PlayerLoomPatternSelectScriptEvent extends BukkitScriptEvent implem
public PlayerLoomPatternSelectScriptEvent() {
registerCouldMatcher("player selects loom pattern");
registerSwitches("type");
+ this.registerOptionalDetermination("pattern", ElementTag.class, (evt, context, pattern) -> {
+ if (Utilities.matchesEnumlike(pattern, PatternType.class)) {
+ evt.event.setPatternType(Utilities.elementToEnumlike(pattern, PatternType.class));
+ return true;
+ }
+ return false;
+ });
}
public PlayerLoomPatternSelectEvent event;
@@ -64,7 +71,7 @@ public boolean matches(ScriptPath path) {
if (!runInCheck(path, event.getLoomInventory().getLocation())) {
return false;
}
- if (!path.tryObjectSwitch("type", new ElementTag(event.getPatternType()))) {
+ if (!path.tryObjectSwitch("type", Utilities.enumlikeToElement(event.getPatternType()))) {
return false;
}
return super.matches(path);
@@ -79,24 +86,11 @@ public ScriptEntryData getScriptEntryData() {
public ObjectTag getContext(String name) {
return switch (name) {
case "loom" -> InventoryTag.mirrorBukkitInventory(event.getLoomInventory());
- case "pattern" -> new ElementTag(event.getPatternType());
+ case "pattern" -> Utilities.enumlikeToElement(event.getPatternType());
default -> super.getContext(name);
};
}
- @Override
- public boolean applyDetermination(ScriptPath path, ObjectTag determinationObj) {
- if (determinationObj instanceof ElementTag) {
- String lower = CoreUtilities.toLowerCase(determinationObj.toString());
- if (lower.startsWith("pattern:")) {
- ElementTag value = new ElementTag(lower.substring("pattern:".length()));
- event.setPatternType(value.asEnum(PatternType.class));
- return true;
- }
- }
- return super.applyDetermination(path, determinationObj);
- }
-
@EventHandler
public void onPlayerSelectsLoomPattern(PlayerLoomPatternSelectEvent event) {
this.event = event;
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerNameEntityScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerNameEntityScriptEvent.java
new file mode 100644
index 0000000000..41d421bd78
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerNameEntityScriptEvent.java
@@ -0,0 +1,99 @@
+package com.denizenscript.denizen.paper.events;
+
+import com.denizenscript.denizen.events.BukkitScriptEvent;
+import com.denizenscript.denizen.objects.EntityTag;
+import com.denizenscript.denizen.paper.PaperModule;
+import com.denizenscript.denizen.utilities.PaperAPITools;
+import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
+import com.denizenscript.denizencore.objects.ObjectTag;
+import com.denizenscript.denizencore.objects.core.ElementTag;
+import com.denizenscript.denizencore.scripts.ScriptEntryData;
+import io.papermc.paper.event.player.PlayerNameEntityEvent;
+import net.md_5.bungee.api.ChatColor;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+public class PlayerNameEntityScriptEvent extends BukkitScriptEvent implements Listener {
+
+ // <--[event]
+ // @Events
+ // player names
+ //
+ // @Location true
+ //
+ // @Plugin Paper
+ //
+ // @Group Paper
+ //
+ // @Cancellable true
+ //
+ // @Triggers when a player attempts to rename an entity with a name tag.
+ //
+ // @Context
+ // returns an EntityTag of the renamed entity.
+ // returns the old name of the entity, if any.
+ // returns the new name of the entity.
+ // returns whether this will cause the entity to persist through server restarts.
+ //
+ // @Determine
+ // "NAME:" to set a different name for the entity.
+ // "PERSISTENT:" to set whether the event will cause the entity to persist through restarts. NOTE: Entities may still persist for other reasons. To ensure they do not, use <@link mechanism EntityTag.force_no_persist>.
+ //
+ // @Player Always.
+ //
+ // -->
+
+ public PlayerNameEntityScriptEvent() {
+ registerCouldMatcher("player names ");
+ this.registerOptionalDetermination("persistent", ElementTag.class, (evt, context, determination) -> {
+ if (determination.isBoolean()) {
+ evt.event.setPersistent(determination.asBoolean());
+ return true;
+ }
+ return false;
+ });
+ this.registerDetermination("name", ElementTag.class, (evt, context, determination) -> {
+ evt.event.setName(PaperModule.parseFormattedText(determination.toString(), ChatColor.WHITE));
+ });
+ }
+
+ public PlayerNameEntityEvent event;
+ public EntityTag entity;
+ public ElementTag oldName;
+
+ @Override
+ public boolean matches(ScriptPath path) {
+ if (!runInCheck(path, entity.getLocation())) {
+ return false;
+ }
+ if (!path.tryArgObject(2, entity)) {
+ return false;
+ }
+ return super.matches(path);
+ }
+
+ @Override
+ public ScriptEntryData getScriptEntryData() {
+ return new BukkitScriptEntryData(event.getPlayer());
+ }
+
+ @Override
+ public ObjectTag getContext(String name) {
+ return switch (name) {
+ case "entity" -> entity.getDenizenObject();
+ case "name" -> new ElementTag(PaperModule.stringifyComponent(event.getName()), true);
+ case "old_name" -> oldName;
+ case "persistent" -> new ElementTag(event.isPersistent());
+ default -> super.getContext(name);
+ };
+ }
+
+ @EventHandler
+ public void playerNamesEntity(PlayerNameEntityEvent event) {
+ this.event = event;
+ entity = new EntityTag(event.getEntity());
+ String name = PaperAPITools.instance.getCustomName(entity.getBukkitEntity());
+ oldName = name == null ? null : new ElementTag(name, true);
+ fire(event);
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerPreparesGrindstoneCraftScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerPreparesGrindstoneCraftScriptEvent.java
index 00ec129dfd..f28063ba49 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerPreparesGrindstoneCraftScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerPreparesGrindstoneCraftScriptEvent.java
@@ -4,9 +4,7 @@
import com.denizenscript.denizen.objects.*;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
import com.denizenscript.denizencore.objects.ObjectTag;
-import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
-import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.destroystokyo.paper.event.inventory.PrepareResultEvent;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.EventHandler;
@@ -48,6 +46,9 @@ public class PlayerPreparesGrindstoneCraftScriptEvent extends BukkitScriptEvent
public PlayerPreparesGrindstoneCraftScriptEvent() {
registerCouldMatcher("player prepares grindstone craft
- ");
+ this.registerDetermination("result", ItemTag.class, (evt, context, item) -> {
+ evt.event.setResult(item.getItemStack());
+ });
}
public PrepareResultEvent event;
@@ -64,20 +65,6 @@ public boolean matches(ScriptPath path) {
return super.matches(path);
}
- @Override
- public boolean applyDetermination(ScriptPath path, ObjectTag determinationObj) {
- if (determinationObj instanceof ElementTag) {
- String determination = determinationObj.toString();
- String lower = CoreUtilities.toLowerCase(determination);
- if (lower.startsWith("result:")) {
- ItemTag result = ItemTag.valueOf(determination.substring("result:".length()), path.container);
- event.setResult(result.getItemStack());
- return true;
- }
- }
- return super.applyDetermination(path, determinationObj);
- }
-
@Override
public ObjectTag getContext(String name) {
return switch (name) {
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerQuitsScriptEventPaperImpl.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerQuitsScriptEventPaperImpl.java
new file mode 100644
index 0000000000..a899af3781
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerQuitsScriptEventPaperImpl.java
@@ -0,0 +1,38 @@
+package com.denizenscript.denizen.paper.events;
+
+import com.denizenscript.denizen.events.player.PlayerQuitsScriptEvent;
+import com.denizenscript.denizen.paper.PaperModule;
+import com.denizenscript.denizencore.objects.ObjectTag;
+import com.denizenscript.denizencore.objects.core.ElementTag;
+import net.md_5.bungee.api.ChatColor;
+
+
+public class PlayerQuitsScriptEventPaperImpl extends PlayerQuitsScriptEvent {
+
+ public PlayerQuitsScriptEventPaperImpl() {
+ registerSwitches("cause");
+ this.registerTextDetermination("none", (evt) -> {
+ event.quitMessage(null);
+ });
+ this.registerDetermination(null, ElementTag.class, (evt, context, determination) -> {
+ event.quitMessage(PaperModule.parseFormattedText(determination.toString(), ChatColor.WHITE));
+ });
+ }
+
+ @Override
+ public boolean matches(ScriptPath path) {
+ if (!runGenericSwitchCheck(path, "cause", event.getReason().name())) {
+ return false;
+ }
+ return super.matches(path);
+ }
+
+ @Override
+ public ObjectTag getContext(String name) {
+ return switch (name) {
+ case "message" -> new ElementTag(PaperModule.stringifyComponent(event.quitMessage()));
+ case "cause" -> new ElementTag(event.getReason());
+ default -> super.getContext(name);
+ };
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerReceivesLinksScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerReceivesLinksScriptEvent.java
new file mode 100644
index 0000000000..90bd7d51e8
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerReceivesLinksScriptEvent.java
@@ -0,0 +1,69 @@
+package com.denizenscript.denizen.paper.events;
+
+import com.denizenscript.denizen.events.BukkitScriptEvent;
+import com.denizenscript.denizen.objects.PlayerTag;
+import com.denizenscript.denizen.utilities.Utilities;
+import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
+import com.denizenscript.denizencore.objects.core.ListTag;
+import com.denizenscript.denizencore.scripts.ScriptEntryData;
+import io.papermc.paper.connection.PlayerConfigurationConnection;
+import io.papermc.paper.connection.PlayerGameConnection;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerLinksSendEvent;
+
+public class PlayerReceivesLinksScriptEvent extends BukkitScriptEvent implements Listener {
+
+ // <--[event]
+ // @Events
+ // player receives links
+ //
+ // @Group Paper
+ //
+ // @Plugin Paper
+ //
+ // @Triggers when a player receives a list of server links.
+ //
+ // @Determine
+ // "LINKS:" to set the links sent to the player. Each item in the list must be a MapTag in <@link language Server Links Format>.
+ // "ADD_LINKS:" to send additional links to the player. Each item in the list must be a MapTag in <@link language Server Links Format>.
+ //
+ // @Player Always.
+ //
+ // @Warning this may fire early in the player login process, during which the linked player is essentially an offline player.
+ //
+ // -->
+
+ public PlayerLinksSendEvent event;
+ public PlayerTag player;
+
+ public PlayerReceivesLinksScriptEvent() {
+ registerCouldMatcher("player receives links");
+ this.registerDetermination("links", ListTag.class, (evt, context, value) -> {
+ Utilities.replaceServerLinks(evt.event.getLinks(), value, context);
+ });
+ this.registerDetermination("add_links", ListTag.class, (evt, context, value) -> {
+ Utilities.fillServerLinks(evt.event.getLinks(), value, context);
+ });
+ }
+
+ @Override
+ public ScriptEntryData getScriptEntryData() {
+ return new BukkitScriptEntryData(player, null);
+ }
+
+ @EventHandler
+ public void onPlayerLinksSend(PlayerLinksSendEvent event) {
+ if (event.getConnection() instanceof PlayerGameConnection gameConnection) {
+ player = new PlayerTag(gameConnection.getPlayer());
+ }
+ else if (event.getConnection() instanceof PlayerConfigurationConnection configConnection) {
+ player = new PlayerTag(configConnection.getProfile().getId());
+ }
+ else {
+ throw new IllegalStateException("Links send event fired with unknown connection type! " + event.getConnection() + " / " + event.getConnection().getClass().getName());
+ }
+ this.event = event;
+ fire(event);
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerSetSpawnScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerSetSpawnScriptEvent.java
new file mode 100644
index 0000000000..7f68012369
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerSetSpawnScriptEvent.java
@@ -0,0 +1,111 @@
+package com.denizenscript.denizen.paper.events;
+
+import com.denizenscript.denizen.events.BukkitScriptEvent;
+import com.denizenscript.denizen.objects.LocationTag;
+import com.denizenscript.denizen.paper.PaperModule;
+import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
+import com.denizenscript.denizencore.objects.ObjectTag;
+import com.denizenscript.denizencore.objects.core.ElementTag;
+import com.denizenscript.denizencore.scripts.ScriptEntryData;
+import com.destroystokyo.paper.event.player.PlayerSetSpawnEvent;
+import net.md_5.bungee.api.ChatColor;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+public class PlayerSetSpawnScriptEvent extends BukkitScriptEvent implements Listener {
+
+ // <--[event]
+ // @Events
+ // player sets spawn
+ //
+ // @Cancellable true
+ //
+ // @Location true
+ //
+ // @Plugin Paper
+ //
+ // @Group Paper
+ //
+ // @Triggers when a player's spawn point changes.
+ //
+ // @Switch cause: to only process when the cause for the event matches the input.
+ //
+ // @Context
+ // returns the reason the player's spawn point changed. A list of causes can be found at <@link url https://jd.papermc.io/paper/1.21.5/com/destroystokyo/paper/event/player/PlayerSetSpawnEvent.Cause.html>.
+ // returns whether this event will persist through source block (bed or respawn anchor) removal.
+ // returns a LocationTag of the new respawn location, if any.
+ // returns the notification message that is sent to the player.
+ // returns whether the player will be notified their spawn point changed.
+ //
+ // @Determine
+ // "FORCED:" to set whether the player's new spawnpoint will persist even if the bed or respawn anchor that triggered the event is removed.
+ // "MESSAGE:" to set the notification message that is sent to the player.
+ // "NOTIFY:" to set whether the player will be notified their spawnpoint changed.
+ // LocationTag to change the respawn location.
+ //
+ // @Player Always.
+ //
+ // -->
+
+ public PlayerSetSpawnScriptEvent() {
+ registerCouldMatcher("player sets spawn");
+ registerSwitches("cause");
+ this.registerOptionalDetermination("forced", ElementTag.class, (evt, context, value) -> {
+ if (value.isBoolean()) {
+ evt.event.setForced(value.asBoolean());
+ return true;
+ }
+ return false;
+ });
+ this.registerDetermination("message", ElementTag.class, (evt, context, message) -> {
+ evt.event.setNotification(PaperModule.parseFormattedText(message.toString(), ChatColor.WHITE));
+ });
+ this.registerOptionalDetermination("notify", ElementTag.class, (evt, context, value) -> {
+ if (value.isBoolean()) {
+ evt.event.setNotifyPlayer(value.asBoolean());
+ return true;
+ }
+ return false;
+ });
+ this.registerDetermination(null, LocationTag.class, (evt, context, location) -> {
+ evt.event.setLocation(location);
+ evt.event.setForced(true); // required if the cause is a bed or respawn anchor
+ });
+ }
+
+ public PlayerSetSpawnEvent event;
+
+ @Override
+ public boolean matches(ScriptPath path) {
+ if (!runInCheck(path, event.getLocation())) {
+ return false;
+ }
+ if (!runGenericSwitchCheck(path, "cause", event.getCause().toString())) {
+ return false;
+ }
+ return super.matches(path);
+ }
+
+ @Override
+ public ScriptEntryData getScriptEntryData() {
+ return new BukkitScriptEntryData(event.getPlayer());
+ }
+
+ @Override
+ public ObjectTag getContext(String name) {
+ return switch (name) {
+ case "cause" -> new ElementTag(event.getCause());
+ case "forced" -> new ElementTag(event.isForced());
+ case "location" -> event.getLocation() != null ? new LocationTag(event.getLocation()) : null;
+ case "message" -> event.getNotification() != null ? new ElementTag(PaperModule.stringifyComponent(event.getNotification()), true) : null;
+ case "notify" -> new ElementTag(event.willNotifyPlayer());
+ default -> super.getContext(name);
+ };
+ }
+
+ @EventHandler
+ public void onPlayerSetsSpawn(PlayerSetSpawnEvent event) {
+ this.event = event;
+ fire(event);
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerShieldDisableScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerShieldDisableScriptEvent.java
new file mode 100644
index 0000000000..89e1358e66
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerShieldDisableScriptEvent.java
@@ -0,0 +1,79 @@
+package com.denizenscript.denizen.paper.events;
+
+import com.denizenscript.denizen.events.BukkitScriptEvent;
+import com.denizenscript.denizen.objects.EntityTag;
+import com.denizenscript.denizen.objects.LocationTag;
+import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
+import com.denizenscript.denizencore.objects.ObjectTag;
+import com.denizenscript.denizencore.objects.core.DurationTag;
+import com.denizenscript.denizencore.scripts.ScriptEntryData;
+import io.papermc.paper.event.player.PlayerShieldDisableEvent;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+public class PlayerShieldDisableScriptEvent extends BukkitScriptEvent implements Listener {
+
+ // <--[event]
+ // @Events
+ // player shield disables
+ //
+ // @Plugin Paper
+ //
+ // @Group Paper
+ //
+ // @Location true
+ //
+ // @Cancellable true
+ //
+ // @Triggers When a players shield is disabled.
+ //
+ // @Context
+ // returns an EntityTag of the attacker who disabled the shield.
+ // returns a DurationTag of the cooldown the shield is disabled for.
+ //
+ // @Determine
+ // "COOLDOWN:" to change the cooldown.
+ //
+ // @Player Always.
+ //
+ // -->
+
+ public PlayerShieldDisableScriptEvent() {
+ registerCouldMatcher("player shield disables");
+ this.registerDetermination("cooldown", DurationTag.class, (evt, context, duration) -> {
+ evt.event.setCooldown(duration.getTicksAsInt());
+ });
+ }
+
+ public PlayerShieldDisableEvent event;
+ public LocationTag location;
+
+ @Override
+ public boolean matches(ScriptPath path) {
+ if (!runInCheck(path, location)) {
+ return false;
+ }
+ return super.matches(path);
+ }
+
+ @Override
+ public ObjectTag getContext(String name) {
+ return switch (name) {
+ case "damager" -> new EntityTag(event.getDamager()).getDenizenObject();
+ case "cooldown" -> new DurationTag((long) event.getCooldown());
+ default -> super.getContext(name);
+ };
+ }
+
+ @Override
+ public ScriptEntryData getScriptEntryData() {
+ return new BukkitScriptEntryData(event.getPlayer());
+ }
+
+ @EventHandler
+ public void playerShieldDisableEvent(PlayerShieldDisableEvent event) {
+ location = new LocationTag(event.getPlayer().getLocation());
+ this.event = event;
+ fire(event);
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerTradesWithMerchantScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerTradesWithMerchantScriptEvent.java
index 3dade8522d..f422eb9bc7 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerTradesWithMerchantScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/PlayerTradesWithMerchantScriptEvent.java
@@ -45,6 +45,9 @@ public class PlayerTradesWithMerchantScriptEvent extends BukkitScriptEvent imple
public PlayerTradesWithMerchantScriptEvent() {
registerCouldMatcher("player trades with merchant");
registerSwitches("result");
+ this.registerDetermination(null, TradeTag.class, (evt, context, trade) -> {
+ evt.event.setTrade(trade.getRecipe());
+ });
}
public PlayerPurchaseEvent event;
@@ -60,15 +63,6 @@ public boolean matches(ScriptPath path) {
return super.matches(path);
}
- @Override
- public boolean applyDetermination(ScriptPath path, ObjectTag determinationObj) {
- if (determinationObj.canBeType(TradeTag.class)) {
- event.setTrade(determinationObj.asType(TradeTag.class, getTagContext(path)).getRecipe());
- return true;
- }
- return super.applyDetermination(path, determinationObj);
- }
-
@Override
public ScriptEntryData getScriptEntryData() {
return new BukkitScriptEntryData(event.getPlayer());
@@ -76,13 +70,11 @@ public ScriptEntryData getScriptEntryData() {
@Override
public ObjectTag getContext(String name) {
- if (name.equals("merchant") && event instanceof PlayerTradeEvent) {
- return new EntityTag(((PlayerTradeEvent) event).getVillager());
- }
- else if (name.equals("trade")) {
- return new TradeTag(event.getTrade()).duplicate();
- }
- return super.getContext(name);
+ return switch (name) {
+ case "merchant" -> event instanceof PlayerTradeEvent tradeEvent ? new EntityTag(tradeEvent.getVillager()) : null;
+ case "trade" -> new TradeTag(event.getTrade()).duplicate();
+ default -> super.getContext(name);
+ };
}
@EventHandler
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/ProjectileCollideScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/ProjectileCollideScriptEvent.java
index 4474983ba4..2e32f403be 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/ProjectileCollideScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/ProjectileCollideScriptEvent.java
@@ -26,7 +26,7 @@ public class ProjectileCollideScriptEvent extends BukkitScriptEvent implements L
//
// @Cancellable true
//
- // @Triggers when a projectile entity collides with an entity (before any damage calculations are done).
+ // @Triggers N/A - use <@link event projectile hits> with the 'entity' switch on versions above 1.19.
//
// @Context
// returns the projectile that is colliding.
@@ -35,7 +35,7 @@ public class ProjectileCollideScriptEvent extends BukkitScriptEvent implements L
// @Player When the entity collided with is a player.
// @NPC When the entity collided with is a NPC.
//
- // @deprecated Use <@link event projectile hits> with the 'entity' switch on versions above 1.19.
+ // @deprecated Use 'projectile hits' with the 'entity' switch on versions above 1.19.
//
// -->
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/ServerListPingScriptEventPaperImpl.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/ServerListPingScriptEventPaperImpl.java
index 38c194a6da..6ea097a13d 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/ServerListPingScriptEventPaperImpl.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/ServerListPingScriptEventPaperImpl.java
@@ -1,6 +1,9 @@
package com.denizenscript.denizen.paper.events;
import com.denizenscript.denizen.events.server.ListPingScriptEvent;
+import com.denizenscript.denizen.nms.NMSHandler;
+import com.denizenscript.denizen.nms.NMSVersion;
+import com.denizenscript.denizen.nms.abstracts.ProfileEditor;
import com.denizenscript.denizen.objects.PlayerTag;
import com.denizenscript.denizen.paper.PaperModule;
import com.denizenscript.denizencore.objects.ObjectTag;
@@ -26,41 +29,67 @@ public class ServerListPingScriptEventPaperImpl extends ListPingScriptEvent {
public ServerListPingScriptEventPaperImpl() {
this.registerOptionalDetermination("protocol_version", ElementTag.class, (evt, context, version) -> {
if (version.isInt()) {
- ((PaperServerListPingEvent) evt.event).setProtocolVersion(version.asInt());
+ evt.getEvent().setProtocolVersion(version.asInt());
return true;
}
return false;
});
this.registerDetermination("version_name", ElementTag.class, (evt, context, name) -> {
- ((PaperServerListPingEvent) evt.event).setVersion(name.toString());
+ evt.getEvent().setVersion(name.asString());
});
this.registerDetermination("exclude_players", ListTag.class, (evt, context, list) -> {
HashSet exclusions = new HashSet<>();
for (PlayerTag player : list.filter(PlayerTag.class, context)) {
exclusions.add(player.getUUID());
}
- Iterator players = ((PaperServerListPingEvent) evt.event).iterator();
- while (players.hasNext()) {
- if (exclusions.contains(players.next().getUniqueId())) {
- players.remove();
+ if (NMSHandler.getVersion().isAtMost(NMSVersion.v1_19)) {
+ Iterator players = evt.getEvent().iterator();
+ while (players.hasNext()) {
+ if (exclusions.contains(players.next().getUniqueId())) {
+ players.remove();
+ }
}
+ return;
}
+ ListedPlayersEditor.excludeListedPlayers(evt.getEvent(), exclusions);
});
this.registerOptionalDetermination("alternate_player_text", ListTag.class, (evt, context, text) -> {
if (!CoreConfiguration.allowRestrictedActions) {
Debug.echoError("Cannot use 'alternate_player_text' in list ping event: 'Allow restricted actions' is disabled in Denizen config.yml.");
return false;
}
- ((PaperServerListPingEvent) evt.event).getPlayerSample().clear();
- for (String line : text) {
- FakeProfile lineProf = new FakeProfile();
- lineProf.setName(line);
- ((PaperServerListPingEvent) evt.event).getPlayerSample().add(lineProf);
+ if (NMSHandler.getVersion().isAtMost(NMSVersion.v1_19)) {
+ evt.getEvent().getPlayerSample().clear();
+ for (String line : text) {
+ FakeProfile lineProf = new FakeProfile();
+ lineProf.setName(line);
+ evt.getEvent().getPlayerSample().add(lineProf);
+ }
+ return true;
}
+ ListedPlayersEditor.setListedPlayerInfo(evt.getEvent(), text);
return true;
});
}
+ public PaperServerListPingEvent getEvent() {
+ return (PaperServerListPingEvent) event;
+ }
+
+ // TODO: workaround for Java trying to load ListedPlayerInfo on old versions, remove once 1.20 is the minimum supported version
+ public static class ListedPlayersEditor {
+ public static void setListedPlayerInfo(PaperServerListPingEvent event, List lines) {
+ event.getListedPlayers().clear();
+ for (String line : lines) {
+ event.getListedPlayers().add(new PaperServerListPingEvent.ListedPlayerInfo(line, ProfileEditor.NIL_UUID));
+ }
+ }
+
+ public static void excludeListedPlayers(PaperServerListPingEvent event, Set exclude) {
+ event.getListedPlayers().removeIf(listedPlayerInfo -> exclude.contains(listedPlayerInfo.id()));
+ }
+ }
+
public static class FakeProfile implements PlayerProfile {
public String name;
@Override public @Nullable String getName() {
@@ -84,7 +113,7 @@ public static class FakeProfile implements PlayerProfile {
@Override public void clearProperties() { }
@Override public boolean isComplete() { return false; }
@Override public @NotNull CompletableFuture update() { return null; }
- @Override public org.bukkit.profile.@NotNull PlayerProfile clone() { return null; }
+ @Override public com.destroystokyo.paper.profile.@NotNull PlayerProfile clone() { return null; }
@Override public boolean completeFromCache() { return false; }
@Override public boolean completeFromCache(boolean b) { return false; }
@Override public boolean completeFromCache(boolean b, boolean b1) { return false; }
@@ -101,10 +130,10 @@ public void setMotd(String text) {
@Override
public ObjectTag getContext(String name) {
return switch (name) {
- case "motd" -> new ElementTag(PaperModule.stringifyComponent(event.motd()));
- case "protocol_version" -> new ElementTag(((PaperServerListPingEvent) event).getProtocolVersion());
- case "version_name" -> new ElementTag(((PaperServerListPingEvent) event).getVersion());
- case "client_protocol_version" -> new ElementTag(((PaperServerListPingEvent) event).getClient().getProtocolVersion());
+ case "motd" -> new ElementTag(PaperModule.stringifyComponent(event.motd()), true);
+ case "protocol_version" -> new ElementTag(getEvent().getProtocolVersion());
+ case "version_name" -> new ElementTag(getEvent().getVersion(), true);
+ case "client_protocol_version" -> new ElementTag(getEvent().getClient().getProtocolVersion());
default -> super.getContext(name);
};
}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/TargetBlockHitScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/TargetBlockHitScriptEvent.java
new file mode 100644
index 0000000000..332ac98dbf
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/TargetBlockHitScriptEvent.java
@@ -0,0 +1,83 @@
+package com.denizenscript.denizen.paper.events;
+
+import com.denizenscript.denizen.events.BukkitScriptEvent;
+import com.denizenscript.denizen.objects.EntityTag;
+import com.denizenscript.denizen.objects.LocationTag;
+import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
+import com.denizenscript.denizencore.objects.ObjectTag;
+import com.denizenscript.denizencore.objects.core.ElementTag;
+import com.denizenscript.denizencore.scripts.ScriptEntryData;
+import io.papermc.paper.event.block.TargetHitEvent;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+
+public class TargetBlockHitScriptEvent extends BukkitScriptEvent implements Listener {
+
+ // <--[event]
+ // @Events
+ // target block hit
+ //
+ // @Location true
+ //
+ // @Group Paper
+ //
+ // @Plugin Paper
+ //
+ // @Triggers when a target block is hit by a projectile such as an arrow.
+ //
+ // @Context
+ // returns an EntityTag of the projectile.
+ // returns a LocationTag of the block that was hit.
+ // returns a LocationTag vector of the hit normal (like '0,1,0' if the projectile hit the top of a block).
+ // returns an EntityTag of the entity that shot the projectile, if any.
+ // returns a ElementTag of the emitted redstone strength.
+ //
+ // @Player when the shooter is a player.
+ //
+ // @NPC when the shooter is a npc.
+ // -->
+
+ public TargetBlockHitScriptEvent() {
+ registerCouldMatcher("target block hit");
+ }
+
+ public TargetHitEvent event;
+ public LocationTag hitBlock;
+ public EntityTag projectile;
+ public EntityTag shooter;
+
+ @Override
+ public boolean matches(ScriptPath path) {
+ if (!runInCheck(path, hitBlock)) {
+ return false;
+ }
+ return super.matches(path);
+ }
+
+ @Override
+ public ObjectTag getContext(String name) {
+ return switch (name) {
+ case "projectile" -> projectile.getDenizenObject();
+ case "hit_block" -> hitBlock;
+ case "hit_face" -> event.getHitBlockFace() != null ? new LocationTag(event.getHitBlockFace().getDirection()) : null;
+ case "shooter" -> shooter != null ? shooter.getDenizenObject() : null;
+ case "strength" -> new ElementTag(event.getSignalStrength());
+ default -> super.getContext(name);
+ };
+ }
+
+ @Override
+ public ScriptEntryData getScriptEntryData() {
+ return new BukkitScriptEntryData(shooter);
+ }
+
+ @EventHandler
+ public void onProjectileHit(TargetHitEvent event) {
+ this.event = event;
+ projectile = new EntityTag(event.getEntity());
+ hitBlock = new LocationTag(event.getHitBlock().getLocation());
+ shooter = projectile.getShooter();
+ fire(event);
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/UnknownCommandScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/UnknownCommandScriptEvent.java
index e6ed7000f9..c800cf62e0 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/UnknownCommandScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/UnknownCommandScriptEvent.java
@@ -4,13 +4,14 @@
import com.denizenscript.denizen.objects.EntityTag;
import com.denizenscript.denizen.objects.LocationTag;
import com.denizenscript.denizen.objects.PlayerTag;
+import com.denizenscript.denizen.paper.PaperModule;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
import com.denizenscript.denizencore.objects.ArgumentHelper;
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.objects.core.ListTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
-import com.denizenscript.denizencore.utilities.CoreUtilities;
+import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.entity.minecart.CommandMinecart;
@@ -53,6 +54,12 @@ public class UnknownCommandScriptEvent extends BukkitScriptEvent implements List
public UnknownCommandScriptEvent() {
registerCouldMatcher("command unknown");
+ this.registerDetermination(null, ElementTag.class, (evt, context, text) -> {
+ evt.event.message(PaperModule.parseFormattedText(text.toString(), ChatColor.WHITE));
+ });
+ this.registerTextDetermination("none", (evt) -> {
+ evt.event.message(null);
+ });
}
public UnknownCommandEvent event;
@@ -60,53 +67,24 @@ public UnknownCommandScriptEvent() {
public String rawArgs;
public String sourceType;
- @Override
- public boolean applyDetermination(ScriptPath path, ObjectTag determinationObj) {
- if (determinationObj instanceof ElementTag) {
- String determination = determinationObj.toString();
- if (CoreUtilities.equalsIgnoreCase(determination, "none")) {
- event.setMessage(null);
- }
- else {
- event.setMessage(determination);
- }
- return true;
- }
- return super.applyDetermination(path, determinationObj);
- }
-
@Override
public ScriptEntryData getScriptEntryData() {
- return new BukkitScriptEntryData(event.getSender() instanceof Player ? new PlayerTag((Player) event.getSender()) : null, null);
+ return new BukkitScriptEntryData(event.getSender() instanceof Player player ? new PlayerTag(player) : null, null);
}
@Override
public ObjectTag getContext(String name) {
- if (name.equals("command")) {
- return new ElementTag(command);
- }
- else if (name.equals("raw_args")) {
- return new ElementTag(rawArgs);
- }
- else if (name.equals("args")) {
- return new ListTag(Arrays.asList(ArgumentHelper.buildArgs(rawArgs, false)));
- }
- else if (name.equals("server")) {
- return new ElementTag(sourceType.equals("server"));
- }
- else if (name.equals("source_type")) {
- return new ElementTag(sourceType);
- }
- else if (name.equals("command_block_location") && sourceType.equals("command_block")) {
- return new LocationTag(((BlockCommandSender) event.getSender()).getBlock().getLocation());
- }
- else if (name.equals("command_minecart") && sourceType.equals("command_minecart")) {
- return new EntityTag((CommandMinecart) event.getSender());
- }
- else if (name.equals("message")) {
- return new ElementTag(event.getMessage());
- }
- return super.getContext(name);
+ return switch (name) {
+ case "command" -> new ElementTag(command, true);
+ case "raw_args" -> new ElementTag(rawArgs, true);
+ case "args" -> new ListTag(Arrays.asList(ArgumentHelper.buildArgs(rawArgs, false)), true);
+ case "server" -> new ElementTag(sourceType.equals("server"));
+ case "source_type" -> new ElementTag(sourceType, true);
+ case "command_block_location" -> sourceType.equals("command_block") ? new LocationTag(((BlockCommandSender) event.getSender()).getBlock().getLocation()) : null;
+ case "command_minecart" -> sourceType.equals("command_minecart") ? new EntityTag((CommandMinecart) event.getSender()) : null;
+ case "message" -> new ElementTag(PaperModule.stringifyComponent(event.message()), true);
+ default -> super.getContext(name);
+ };
}
@EventHandler
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/events/WorldGameRuleChangeScriptEvent.java b/paper/src/main/java/com/denizenscript/denizen/paper/events/WorldGameRuleChangeScriptEvent.java
index 74f9555a86..64fc3dbe05 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/events/WorldGameRuleChangeScriptEvent.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/events/WorldGameRuleChangeScriptEvent.java
@@ -7,10 +7,10 @@
import com.denizenscript.denizen.objects.PlayerTag;
import com.denizenscript.denizen.objects.WorldTag;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
+import com.denizenscript.denizen.utilities.world.GameRuleReflect;
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
-import com.denizenscript.denizencore.utilities.CoreUtilities;
import io.papermc.paper.event.world.WorldGameRuleChangeEvent;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
@@ -19,7 +19,6 @@
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
-
public class WorldGameRuleChangeScriptEvent extends BukkitScriptEvent implements Listener {
// <--[event]
@@ -52,6 +51,13 @@ public class WorldGameRuleChangeScriptEvent extends BukkitScriptEvent implements
public WorldGameRuleChangeScriptEvent() {
registerCouldMatcher("gamerule changes (in )");
registerSwitches("gamerule");
+ this.registerOptionalDetermination("value", ElementTag.class, (evt, context, value) -> {
+ if (value.isBoolean() || value.isInt()) {
+ evt.event.setValue(value.toString());
+ return true;
+ }
+ return false;
+ });
}
public WorldGameRuleChangeEvent event;
@@ -64,7 +70,7 @@ public boolean matches(ScriptPath path) {
if (path.eventArgLowerAt(2).equals("in") && !path.tryArgObject(3, world)) {
return false;
}
- if (!runGenericSwitchCheck(path, "gamerule", event.getGameRule().getName())) {
+ if (!runGenericSwitchCheck(path, "gamerule", GameRuleReflect.getName(event.getGameRule()))) {
return false;
}
return super.matches(path);
@@ -72,30 +78,15 @@ public boolean matches(ScriptPath path) {
@Override
public ObjectTag getContext(String name) {
- switch (name) {
- case "gamerule": return new ElementTag(event.getGameRule().getName());
- case "value": return new ElementTag(event.getValue());
- case "source_type": return getSourceType();
- case "command_block_location": return getCommandBlock();
- case "command_minecart": return getCommandMinecart();
- case "world": return world;
- }
- return super.getContext(name);
- }
-
- @Override
- public boolean applyDetermination(ScriptPath path, ObjectTag determinationObj) {
- if (determinationObj instanceof ElementTag) {
- String lower = CoreUtilities.toLowerCase(determinationObj.toString());
- if (lower.startsWith("value:")) {
- ElementTag value = new ElementTag(lower.substring("value:".length()));
- if (value.isInt() || value.isBoolean()) {
- event.setValue(value.toString());
- return true;
- }
- }
- }
- return super.applyDetermination(path, determinationObj);
+ return switch (name) {
+ case "gamerule" -> new ElementTag(GameRuleReflect.getName(event.getGameRule()), true);
+ case "value" -> new ElementTag(event.getValue(), true);
+ case "source_type" -> getSourceType();
+ case "command_block_location" -> getCommandBlock();
+ case "command_minecart" -> getCommandMinecart();
+ case "world" -> world;
+ default -> super.getContext(name);
+ };
}
@Override
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/properties/EntityBodyStingers.java b/paper/src/main/java/com/denizenscript/denizen/paper/properties/EntityBodyStingers.java
new file mode 100644
index 0000000000..209b08520f
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/properties/EntityBodyStingers.java
@@ -0,0 +1,49 @@
+package com.denizenscript.denizen.paper.properties;
+
+import com.denizenscript.denizen.objects.EntityTag;
+import com.denizenscript.denizen.objects.properties.entity.EntityProperty;
+import com.denizenscript.denizencore.objects.Mechanism;
+import com.denizenscript.denizencore.objects.core.ElementTag;
+
+public class EntityBodyStingers extends EntityProperty {
+
+ // <--[property]
+ // @object EntityTag
+ // @name body_stingers
+ // @input ElementTag(Number)
+ // @plugin Paper
+ // @description
+ // Controls the number of bee stingers stuck in an entity's body.
+ // Note: Bee stingers will only be visible for players or player-type npcs.
+ // -->
+
+ public static boolean describes(EntityTag entity) {
+ return entity.isLivingEntity();
+ }
+
+ @Override
+ public boolean isDefaultValue(ElementTag value) {
+ return value.asInt() == 0;
+ }
+
+ @Override
+ public ElementTag getPropertyValue() {
+ return new ElementTag(getLivingEntity().getBeeStingersInBody());
+ }
+
+ @Override
+ public String getPropertyId() {
+ return "body_stingers";
+ }
+
+ @Override
+ public void setPropertyValue(ElementTag param, Mechanism mechanism) {
+ if (mechanism.requireInteger()) {
+ getLivingEntity().setBeeStingersInBody(param.asInt());
+ }
+ }
+
+ public static void register() {
+ autoRegister("body_stingers", EntityBodyStingers.class, ElementTag.class, false);
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/properties/EntityShouldBurn.java b/paper/src/main/java/com/denizenscript/denizen/paper/properties/EntityShouldBurn.java
index 93a216dcc8..32af74348e 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/properties/EntityShouldBurn.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/properties/EntityShouldBurn.java
@@ -1,12 +1,12 @@
package com.denizenscript.denizen.paper.properties;
+import com.denizenscript.denizen.nms.NMSHandler;
+import com.denizenscript.denizen.nms.NMSVersion;
import com.denizenscript.denizen.objects.EntityTag;
import com.denizenscript.denizen.objects.properties.entity.EntityProperty;
import com.denizenscript.denizencore.objects.Mechanism;
import com.denizenscript.denizencore.objects.core.ElementTag;
-import org.bukkit.entity.Phantom;
-import org.bukkit.entity.Skeleton;
-import org.bukkit.entity.Zombie;
+import org.bukkit.entity.*;
public class EntityShouldBurn extends EntityProperty {
@@ -16,13 +16,15 @@ public class EntityShouldBurn extends EntityProperty {
// @input ElementTag(Boolean)
// @plugin Paper
// @description
- // If the entity is a Zombie, Skeleton, or Phantom, controls whether it should burn in daylight.
+ // If the entity is a Zombie, Skeleton, Stray, Bogged, or Phantom, controls whether it should burn in daylight.
// -->
public static boolean describes(EntityTag entity) {
return entity.getBukkitEntity() instanceof Zombie
+ || entity.getBukkitEntity() instanceof Phantom
|| entity.getBukkitEntity() instanceof Skeleton
- || entity.getBukkitEntity() instanceof Phantom;
+ || entity.getBukkitEntity() instanceof Stray
+ || (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20) && entity.getBukkitEntity() instanceof Bogged);
}
@Override
@@ -30,11 +32,17 @@ public ElementTag getPropertyValue() {
if (getEntity() instanceof Zombie zombie) {
return new ElementTag(zombie.shouldBurnInDay());
}
+ else if (getEntity() instanceof Phantom phantom) {
+ return new ElementTag(phantom.shouldBurnInDay());
+ }
else if (getEntity() instanceof Skeleton skeleton) {
return new ElementTag(skeleton.shouldBurnInDay());
}
- else { // phantom
- return new ElementTag(as(Phantom.class).shouldBurnInDay());
+ else if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20) && getEntity() instanceof Bogged bogged) {
+ return new ElementTag(bogged.shouldBurnInDay());
+ }
+ else { // stray
+ return new ElementTag(as(Stray.class).shouldBurnInDay());
}
}
@@ -49,11 +57,17 @@ public void setPropertyValue(ElementTag param, Mechanism mechanism) {
if (getEntity() instanceof Zombie zombie) {
zombie.setShouldBurnInDay(param.asBoolean());
}
+ else if (getEntity() instanceof Phantom phantom) {
+ phantom.setShouldBurnInDay(param.asBoolean());
+ }
else if (getEntity() instanceof Skeleton skeleton) {
skeleton.setShouldBurnInDay(param.asBoolean());
}
- else { // phantom
- as(Phantom.class).setShouldBurnInDay(param.asBoolean());
+ else if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20) && getEntity() instanceof Bogged bogged) {
+ bogged.setShouldBurnInDay(param.asBoolean());
+ }
+ else { // stray
+ as(Stray.class).setShouldBurnInDay(param.asBoolean());
}
}
}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemovedComponents.java b/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemovedComponents.java
new file mode 100644
index 0000000000..cd272bdda8
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemovedComponents.java
@@ -0,0 +1,86 @@
+package com.denizenscript.denizen.paper.properties;
+
+import com.denizenscript.denizen.objects.ItemTag;
+import com.denizenscript.denizen.objects.properties.item.ItemProperty;
+import com.denizenscript.denizen.paper.datacomponents.DataComponentAdapter;
+import com.denizenscript.denizencore.objects.Mechanism;
+import com.denizenscript.denizencore.objects.core.ElementTag;
+import com.denizenscript.denizencore.objects.core.ListTag;
+import com.denizenscript.denizencore.objects.properties.PropertyParser;
+import io.papermc.paper.datacomponent.DataComponentType;
+
+public class ItemRemovedComponents extends ItemProperty {
+
+ // <--[property]
+ // @object ItemTag
+ // @name removed_components
+ // @input ListTag
+ // @description
+ // Controls the item components explicitly removed from an item.
+ // This can be used to remove item's default behavior, such as making consumable items non-consumable.
+ // Alternatively, use <@link mechanism ItemTag.remove_component> to remove a single component.
+ // See <@link language Item Components> for more information.
+ // -->
+
+ public static boolean describes(ItemTag item) {
+ return !item.getItemStack().isEmpty();
+ }
+
+ public boolean isRemoved(DataComponentType componentType) {
+ return getItemStack().isDataOverridden(componentType) && !getItemStack().hasData(componentType);
+ }
+
+ @Override
+ public ListTag getPropertyValue() {
+ return new ListTag(getMaterial().getDefaultDataTypes(), this::isRemoved, componentType -> new ElementTag(componentType.key().asMinimalString(), true));
+ }
+
+ @Override
+ public boolean isDefaultValue(ListTag value) {
+ return value.isEmpty();
+ }
+
+ @Override
+ public void setPropertyValue(ListTag value, Mechanism mechanism) {
+ for (DataComponentType componentType : getMaterial().getDefaultDataTypes()) {
+ if (isRemoved(componentType)) {
+ getItemStack().resetData(componentType);
+ }
+ }
+ for (String input : value) {
+ DataComponentType componentType = DataComponentAdapter.getComponentType(input);
+ if (componentType == null) {
+ mechanism.echoError("Invalid type to remove '" + input + "' specified: must be a valid property or item component name.");
+ continue;
+ }
+ getItemStack().unsetData(componentType);
+ }
+ }
+
+ @Override
+ public String getPropertyId() {
+ return "removed_components";
+ }
+
+ public static void register() {
+ autoRegister("removed_components", ItemRemovedComponents.class, ListTag.class, false);
+
+ // <--[mechanism]
+ // @object ItemTag
+ // @name remove_component
+ // @input ElementTag
+ // @description
+ // Removes the specified item component from the item, see <@link language Item Components> for more information.
+ // This can be used to remove item's default behavior, such as making consumable items non-consumable.
+ // See also <@link property ItemTag.removed_components>.
+ // -->
+ PropertyParser.registerMechanism(ItemRemovedComponents.class, ElementTag.class, "remove_component", (prop, mechanism, input) -> {
+ DataComponentType componentType = DataComponentAdapter.getComponentType(input.asString());
+ if (componentType == null) {
+ mechanism.echoError("Invalid type to remove specified: must be a valid property or item component name.");
+ return;
+ }
+ prop.getItemStack().unsetData(componentType);
+ });
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperEntityExtensions.java b/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperEntityExtensions.java
index 1fb2c8ddaa..94bcb7f641 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperEntityExtensions.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperEntityExtensions.java
@@ -7,6 +7,8 @@
import com.denizenscript.denizen.objects.LocationTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.objects.core.MapTag;
+import io.papermc.paper.entity.Shearable;
+import net.kyori.adventure.sound.Sound;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ExperienceOrb;
@@ -184,6 +186,47 @@ public static void register() {
}
object.getLivingEntity().damageItemStack(slot.asEnum(EquipmentSlot.class), amount.asInt());
});
+
+ // <--[mechanism]
+ // @object EntityTag
+ // @name shear
+ // @input None
+ // @Plugin paper
+ // @group paper
+ // @description
+ // Shears entities in the same way as a player can do using shears, including drops.
+ // If the entity is not ready to be sheared, there will be no drops but the sound will still play.
+ //
+ // This mech will:
+ // - Shear a sheep
+ // - harvest a bogged
+ // - harvest a mushroom cow (note: entity data will be lost as Minecraft will remove the entity and spawn an entirely new cow instead)
+ // - derp a snowman (i.e. remove the pumpkin)
+ //
+ // Optionally, specify a sound source to change the source of the sound.
+ // Valid sound sources can be found here: <@link url https://jd.advntr.dev/api/latest/net/kyori/adventure/sound/Sound.Source.html>.
+ //
+ // @example
+ // # Shears the entity you're looking at.
+ // - adjust shear
+ //
+ // -->
+ EntityTag.registerSpawnedOnlyMechanism("shear", false, (object, mechanism) -> {
+ if (!(object.getBukkitEntity() instanceof Shearable shearable)) {
+ return;
+ }
+ if (!mechanism.hasValue()) {
+ shearable.shear();
+ return;
+ }
+ ElementTag input = mechanism.getValue();
+ if (!mechanism.requireEnum(Sound.Source.class)) {
+ mechanism.echoError("Invalid sound source specified: " + input);
+ return;
+ }
+ Sound.Source source = input.asEnum(Sound.Source.class);
+ shearable.shear(source);
+ });
}
}
}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperPlayerExtensions.java b/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperPlayerExtensions.java
index fba2df72d6..d0d3806724 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperPlayerExtensions.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperPlayerExtensions.java
@@ -6,8 +6,12 @@
import com.denizenscript.denizen.objects.PlayerTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.objects.core.ListTag;
+import com.denizenscript.denizencore.objects.core.MapTag;
+import com.destroystokyo.paper.ClientOption;
+import com.destroystokyo.paper.SkinParts;
import net.kyori.adventure.util.TriState;
import org.bukkit.Material;
+import org.bukkit.entity.Player;
public class PaperPlayerExtensions {
@@ -26,6 +30,68 @@ public static void register() {
return new ElementTag(object.getPlayerEntity().getAffectsSpawning());
});
+ // <--[tag]
+ // @attribute
+ // @returns MapTag
+ // @group paper
+ // @Plugin Paper
+ // @description
+ // Returns the player's client options.
+ // The output map contains the following keys:
+ // - 'allow_server_listings' (ElementTag(Boolean)): whether the player allows server listings. Available only on MC 1.19+.
+ // - 'chat_colors_enabled' (ElementTag(Boolean)): whether the player has chat colors enabled.
+ // - 'chat_visibility' (ElementTag): the player's current chat visibility option. Possible output values are: FULL, SYSTEM, HIDDEN, and UNKNOWN.
+ // - 'locale' (ElementTag): the player's current locale.
+ // - 'main_hand' (ElementTag): the player's main hand, either LEFT or RIGHT.
+ // - 'skin_parts' (MapTag): which skin parts the player has enabled. The output map contains the following keys:
+ // - 'cape' (ElementTag(Boolean)): whether the player's cape is enabled.
+ // - 'hat' (ElementTag(Boolean)): whether the player's hat is enabled.
+ // - 'jacket' (ElementTag(Boolean)): whether the player's jacket is enabled.
+ // - 'left_sleeve' (ElementTag(Boolean)): whether the player's left sleeve is enabled.
+ // - 'right_sleeve' (ElementTag(Boolean)): whether the player's right sleeve is enabled.
+ // - 'left_pants' (ElementTag(Boolean)): whether the player's left pants is enabled.
+ // - 'right_pants' (ElementTag(Boolean)): whether the player's right pants is enabled.
+ // - 'text_filtering_enabled' (ElementTag(Boolean)): whether the player has text filtering enabled. Available only on MC 1.19+.
+ // - 'view_distance' (ElementTag(Number)): the player's current view distance.
+ // -->
+ PlayerTag.registerOnlineOnlyTag(MapTag.class, "client_options", (attribute, object) -> {
+ MapTag map = new MapTag();
+ Player player = object.getPlayerEntity();
+ if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_19)) {
+ map.putObject("allow_server_listings", new ElementTag(player.getClientOption(ClientOption.ALLOW_SERVER_LISTINGS)));
+ map.putObject("text_filtering_enabled", new ElementTag(player.getClientOption(ClientOption.TEXT_FILTERING_ENABLED)));
+ }
+ map.putObject("chat_colors_enabled", new ElementTag(player.getClientOption(ClientOption.CHAT_COLORS_ENABLED)));
+ map.putObject("chat_visibility", new ElementTag(player.getClientOption(ClientOption.CHAT_VISIBILITY)));
+ map.putObject("locale", new ElementTag(player.getClientOption(ClientOption.LOCALE)));
+ map.putObject("main_hand", new ElementTag(player.getClientOption(ClientOption.MAIN_HAND)));
+ MapTag skinParts = new MapTag();
+ SkinParts parts = player.getClientOption(ClientOption.SKIN_PARTS);
+ skinParts.putObject("cape", new ElementTag(parts.hasCapeEnabled()));
+ skinParts.putObject("hat", new ElementTag(parts.hasHatsEnabled()));
+ skinParts.putObject("jacket", new ElementTag(parts.hasJacketEnabled()));
+ skinParts.putObject("left_sleeve", new ElementTag(parts.hasLeftSleeveEnabled()));
+ skinParts.putObject("right_sleeve", new ElementTag(parts.hasRightSleeveEnabled()));
+ skinParts.putObject("left_pants", new ElementTag(parts.hasLeftPantsEnabled()));
+ skinParts.putObject("right_pants", new ElementTag(parts.hasRightPantsEnabled()));
+ map.putObject("skin_parts", skinParts);
+ map.putObject("view_distance", new ElementTag(player.getClientOption(ClientOption.VIEW_DISTANCE)));
+ return map;
+ });
+
+ // <--[tag]
+ // @attribute
+ // @returns ElementTag(Number)
+ // @mechanism PlayerTag.view_distance
+ // @group paper
+ // @Plugin Paper
+ // @description
+ // Returns this player's view distance, or the view distance of the world they're in if unset.
+ // -->
+ PlayerTag.registerOnlineOnlyTag(ElementTag.class, "view_distance", (attribute, object) -> {
+ return new ElementTag(object.getPlayerEntity().getViewDistance());
+ });
+
// <--[mechanism]
// @object PlayerTag
// @name affects_monster_spawning
@@ -83,6 +149,32 @@ public static void register() {
}
});
+ // <--[mechanism]
+ // @object PlayerTag
+ // @name view_distance
+ // @input ElementTag(Number)
+ // @Plugin Paper
+ // @group paper
+ // @description
+ // Sets this player's view distance. Input must be a number between 2 and 32.
+ // This will be reset when a player rejoins. Provide no input to unset.
+ // @tags
+ //
+ // -->
+ PlayerTag.registerOnlineOnlyMechanism("view_distance", (object, mechanism) -> {
+ if (!mechanism.hasValue()) {
+ object.getPlayerEntity().setViewDistance(-1);
+ }
+ else if (mechanism.requireInteger()) {
+ int distance = mechanism.getValue().asInt();
+ if (distance < 2 || distance > 32) {
+ mechanism.echoError("Invalid view distance '" + distance + "': must be between 2 and 32.");
+ return;
+ }
+ object.getPlayerEntity().setViewDistance(distance);
+ }
+ });
+
if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_19)) {
// <--[tag]
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperWorldExtensions.java b/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperWorldExtensions.java
index 2299f5864e..74c335a853 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperWorldExtensions.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperWorldExtensions.java
@@ -1,8 +1,14 @@
package com.denizenscript.denizen.paper.properties;
+import com.denizenscript.denizen.nms.NMSHandler;
+import com.denizenscript.denizen.nms.NMSVersion;
+import com.denizenscript.denizen.objects.EntityTag;
+import com.denizenscript.denizen.objects.LocationTag;
import com.denizenscript.denizen.objects.WorldTag;
import com.denizenscript.denizen.utilities.BukkitImplDeprecations;
import com.denizenscript.denizencore.objects.core.ElementTag;
+import com.denizenscript.denizencore.objects.core.ListTag;
+import org.bukkit.boss.DragonBattle;
public class PaperWorldExtensions {
@@ -23,6 +29,88 @@ public static void register() {
return new ElementTag(world.getWorld().getNoTickViewDistance());
});
+ if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20)) {
+
+ // <--[tag]
+ // @attribute
+ // @returns ElementTag(Number)
+ // @group paper
+ // @Plugin Paper
+ // @description
+ // Returns the number of end gateway portals.
+ // Only works if the world is an end world.
+ // -->
+ WorldTag.tagProcessor.registerTag(ElementTag.class, "gateway_count", (attribute, object) -> {
+ DragonBattle battle = object.getWorld().getEnderDragonBattle();
+ if (battle == null) {
+ attribute.echoError("Provided world is not an end world!");
+ return null;
+ }
+ return new ElementTag(battle.getGatewayCount());
+ });
+
+ // <--[tag]
+ // @attribute
+ // @returns ListTag(EntityTag)
+ // @group paper
+ // @Plugin Paper
+ // @description
+ // Returns a ListTag of the healing end crystals located on top of the obsidian towers.
+ // Only works if the world is an end world.
+ // -->
+ WorldTag.tagProcessor.registerTag(ListTag.class, "healing_crystals", (attribute, object) -> {
+ DragonBattle battle = object.getWorld().getEnderDragonBattle();
+ if (battle == null) {
+ attribute.echoError("Provided world is not an end world!");
+ return null;
+ }
+ return new ListTag(battle.getHealingCrystals(), EntityTag::new);
+ });
+
+ // <--[tag]
+ // @attribute
+ // @returns ListTag(EntityTag)
+ // @group paper
+ // @Plugin Paper
+ // @description
+ // Returns a ListTag of the respawn end crystals located at the end exit portal.
+ // Only works if the world is an end world.
+ // -->
+ WorldTag.tagProcessor.registerTag(ListTag.class, "respawn_crystals", (attribute, object) -> {
+ DragonBattle battle = object.getWorld().getEnderDragonBattle();
+ if (battle == null) {
+ attribute.echoError("Provided world is not an end world!");
+ return null;
+ }
+ return new ListTag(battle.getRespawnCrystals(), EntityTag::new);
+ });
+
+ // <--[mechanism]
+ // @object WorldTag
+ // @name spawn_gateway
+ // @input LocationTag
+ // @group paper
+ // @Plugin Paper
+ // @description
+ // Spawns a new end gateway portal at the specified location.
+ // If no location is specified, tries to spawn a new end gateway using default game mechanics.
+ // Only works if the world is an end world.
+ // -->
+ WorldTag.tagProcessor.registerMechanism("spawn_gateway", false, (object, mechanism) -> {
+ DragonBattle battle = object.getWorld().getEnderDragonBattle();
+ if (battle == null) {
+ mechanism.echoError("Cannot spawn gateway in non-end world!");
+ return;
+ }
+ if (!mechanism.hasValue()) {
+ battle.spawnNewGateway();
+ }
+ else if (mechanism.requireObject(LocationTag.class)) {
+ battle.spawnNewGateway(mechanism.valueAsType(LocationTag.class));
+ }
+ });
+ }
+
// <--[mechanism]
// @object WorldTag
// @name view_distance
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/tags/PaperTagBase.java b/paper/src/main/java/com/denizenscript/denizen/paper/tags/PaperTagBase.java
index d5f4aef3f9..df7f418def 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/tags/PaperTagBase.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/tags/PaperTagBase.java
@@ -2,29 +2,20 @@
import com.denizenscript.denizencore.objects.core.DurationTag;
import com.denizenscript.denizencore.objects.core.ListTag;
-import com.denizenscript.denizencore.tags.TagRunnable;
-import com.denizenscript.denizencore.tags.Attribute;
-import com.denizenscript.denizencore.tags.ReplaceableTagEvent;
-import com.denizenscript.denizencore.tags.TagManager;
+import com.denizenscript.denizencore.tags.*;
import org.bukkit.Bukkit;
-public class PaperTagBase {
+public class PaperTagBase extends PseudoObjectTagBase {
+
+ public static PaperTagBase instance;
public PaperTagBase() {
- TagManager.registerTagHandler(new TagRunnable.RootForm() {
- @Override
- public void run(ReplaceableTagEvent event) {
- paperTag(event);
- }
- }, "paper");
+ instance = this;
+ TagManager.registerStaticTagBaseHandler(PaperTagBase.class, "paper", (t) -> instance);
}
- public void paperTag(ReplaceableTagEvent event) {
- if (!event.matches("paper") || event.replaced()) {
- return;
- }
-
- Attribute attribute = event.getAttributes().fulfill(1);
+ @Override
+ public void register() {
// <--[tag]
// @attribute
@@ -34,13 +25,12 @@ public void paperTag(ReplaceableTagEvent event) {
// Returns a sample of the server's last 5s of tick times as a list of durations.
// On average, a tick should take 50ms or less for a stable 20tps.
// -->
- if (attribute.startsWith("tick_times")) {
+ tagProcessor.registerTag(ListTag.class, "tick_times", (attribute, object) -> {
ListTag list = new ListTag();
for (long time : Bukkit.getServer().getTickTimes()) {
list.addObject(new DurationTag(time / 1000000000D));
}
- event.setReplacedObject(list.getObjectAttribute(attribute.fulfill(1)));
- return;
- }
+ return list;
+ });
}
}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/BlockTagsSetter.java b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/BlockTagsSetter.java
new file mode 100644
index 0000000000..0215cd070e
--- /dev/null
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/BlockTagsSetter.java
@@ -0,0 +1,94 @@
+package com.denizenscript.denizen.paper.utilities;
+
+import com.denizenscript.denizen.Denizen;
+import com.denizenscript.denizen.utilities.Utilities;
+import com.denizenscript.denizen.utilities.VanillaTagHelper;
+import com.denizenscript.denizencore.utilities.ReflectionHelper;
+import com.denizenscript.denizencore.utilities.debugging.Debug;
+import com.destroystokyo.paper.event.server.ServerTickEndEvent;
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.configuration.PluginMeta;
+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.tag.TagKey;
+import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.block.BlockType;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+import java.lang.invoke.MethodHandle;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class BlockTagsSetter implements Listener {
+
+ public static final MethodHandle BOOTSTRAP_CONTEXT_CONSTRUCTOR;
+
+ static {
+ try {
+ Class> bootstrapContextImplClass = Class.forName("io.papermc.paper.plugin.bootstrap.PluginBootstrapContextImpl");
+ BOOTSTRAP_CONTEXT_CONSTRUCTOR = ReflectionHelper.getConstructor(bootstrapContextImplClass, PluginMeta.class, Path.class, ComponentLogger.class, Path.class);
+ }
+ catch (Throwable e) {
+ throw new RuntimeException("Failed to initialize BlockTagsSetter", e);
+ }
+ }
+
+ public static final BlockTagsSetter INSTANCE = new BlockTagsSetter(Denizen.getInstance());
+
+ public Map, Set>> modifiedTags = new HashMap<>();
+ public boolean batchReloadNeeded;
+
+ public BlockTagsSetter(Denizen plugin) {
+ Bukkit.getPluginManager().registerEvents(this, plugin);
+ try {
+ BootstrapContext fakeContext = (BootstrapContext) BOOTSTRAP_CONTEXT_CONSTRUCTOR.invoke(plugin.getPluginMeta(), plugin.getDataPath(), plugin.getComponentLogger(), plugin.getFile().toPath());
+ fakeContext.getLifecycleManager().registerEventHandler(LifecycleEvents.TAGS.postFlatten(RegistryKey.BLOCK), event -> {
+ Map, Collection>> allTags = event.registrar().getAllTags();
+ for (Map.Entry, Set>> entry : modifiedTags.entrySet()) {
+ TypedKey blockType = entry.getKey();
+ Set> tags = entry.getValue();
+ for (Map.Entry, Collection>> tagEntry : allTags.entrySet()) {
+ TagKey tagKey = tagEntry.getKey();
+ Collection> values = tagEntry.getValue();
+ if (values.contains(blockType) && !tags.contains(tagKey)) {
+ List> modifiedValues = new ArrayList<>(values);
+ modifiedValues.remove(blockType);
+ event.registrar().setTag(tagKey, modifiedValues);
+ }
+ }
+ for (TagKey tag : tags) {
+ event.registrar().addToTag(tag, List.of(blockType));
+ }
+ }
+ });
+ }
+ catch (Throwable e) {
+ Debug.echoError(e);
+ }
+ }
+
+ @EventHandler
+ public void onServerTickEnd(ServerTickEndEvent event) {
+ if (batchReloadNeeded) {
+ batchReloadNeeded = false;
+ Bukkit.reloadData();
+ }
+ }
+
+ public void setTags(Material material, Set tags) {
+ TypedKey blockKey = TypedKey.create(RegistryKey.BLOCK, material.getKey());
+ Set> tagKeys = tags.stream().map(tag -> TagKey.create(RegistryKey.BLOCK, tag)).collect(Collectors.toCollection(HashSet::new));
+ Set> oldTagKeys = modifiedTags.put(blockKey, tagKeys);
+ if (tagKeys.equals(oldTagKeys)) {
+ return;
+ }
+ VanillaTagHelper.tagsByMaterial.put(material, tags.stream().map(Utilities::namespacedKeyToString).collect(Collectors.toCollection(HashSet::new)));
+ batchReloadNeeded = true;
+ }
+}
diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/PaperAPIToolsImpl.java b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/PaperAPIToolsImpl.java
index 76bc8a34c1..0b6d741ced 100644
--- a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/PaperAPIToolsImpl.java
+++ b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/PaperAPIToolsImpl.java
@@ -11,6 +11,9 @@
import com.denizenscript.denizen.utilities.FormattedTextHelper;
import com.denizenscript.denizen.utilities.PaperAPITools;
import com.denizenscript.denizencore.DenizenCore;
+import com.denizenscript.denizencore.objects.Mechanism;
+import com.denizenscript.denizencore.objects.core.ElementTag;
+import com.denizenscript.denizencore.tags.TagContext;
import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.denizenscript.denizencore.utilities.ReflectionHelper;
import com.denizenscript.denizencore.utilities.debugging.Debug;
@@ -18,24 +21,26 @@
import com.destroystokyo.paper.profile.ProfileProperty;
import io.papermc.paper.entity.TeleportFlag;
import io.papermc.paper.potion.PotionMix;
+import io.papermc.paper.world.WeatheringCopperState;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.chat.BaseComponent;
import org.bukkit.*;
import org.bukkit.block.Sign;
import org.bukkit.command.CommandSender;
-import org.bukkit.entity.Entity;
-import org.bukkit.entity.Player;
-import org.bukkit.entity.TextDisplay;
+import org.bukkit.entity.*;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.*;
+import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionBrewer;
import org.bukkit.scoreboard.Team;
import org.bukkit.util.Consumer;
+import java.net.URI;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -79,6 +84,12 @@ public String getCustomName(Entity entity) {
return PaperModule.stringifyComponent(entity.customName());
}
+ @Override
+ public BaseComponent[] getCustomNameComponent(Entity entity) {
+ Component customName = entity.customName();
+ return customName != null ? FormattedTextHelper.parseJson(PaperModule.componentToJson(customName)) : null;
+ }
+
@Override
public void setPlayerListName(Player player, String name) {
player.playerListName(PaperModule.parseFormattedText(name, ChatColor.WHITE));
@@ -155,8 +166,9 @@ public void teleport(Entity entity, Location loc, PlayerTeleportEvent.TeleportCa
}
}
if (relativeTeleportFlags != null) {
+ // TODO: MC 1.21.3: Paper updated this API to work differently due to underlying Minecraft changes.
for (TeleportCommand.Relative relativeTeleportFlag : relativeTeleportFlags) {
- teleportFlags.add(TeleportFlag.Relative.values()[relativeTeleportFlag.ordinal()]);
+ teleportFlags.add(new ElementTag(relativeTeleportFlag.name()).asEnum(TeleportFlag.Relative.class));
}
}
entity.teleport(loc, cause, teleportFlags.toArray(new TeleportFlag[0]));
@@ -170,11 +182,12 @@ public void registerBrewingRecipe(String keyName, ItemStack result, String input
if (!NMSHandler.getVersion().isAtLeast(NMSVersion.v1_18)) {
throw new UnsupportedOperationException();
}
- RecipeChoice inputChoice = parseBrewingRecipeChoice(itemScriptContainer, input);
+ TagContext context = DenizenCore.implementation.getTagContext(itemScriptContainer);
+ RecipeChoice inputChoice = parseBrewingRecipeChoice(itemScriptContainer, input, context);
if (inputChoice == null) {
return;
}
- RecipeChoice ingredientChoice = parseBrewingRecipeChoice(itemScriptContainer, ingredient);
+ RecipeChoice ingredientChoice = parseBrewingRecipeChoice(itemScriptContainer, ingredient, context);
if (ingredientChoice == null) {
return;
}
@@ -195,10 +208,10 @@ public void clearBrewingRecipes() {
}
}
- public static RecipeChoice parseBrewingRecipeChoice(ItemScriptContainer container, String choice) {
+ public static RecipeChoice parseBrewingRecipeChoice(ItemScriptContainer container, String choice, TagContext context) {
if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20) && choice.startsWith("matcher:")) {
String matcher = choice.substring("matcher:".length());
- return PotionMix.createPredicateChoice(item -> new ItemTag(item).tryAdvancedMatcher(matcher));
+ return PotionMix.createPredicateChoice(item -> new ItemTag(item).tryAdvancedMatcher(matcher, context));
}
boolean exact = true;
if (choice.startsWith("material:")) {
@@ -377,4 +390,45 @@ public String getClientBrand(Player player) {
String clientBrand = player.getClientBrandName();
return clientBrand != null ? clientBrand : "unknown";
}
+
+ @Override
+ public boolean canUseEquipmentSlot(LivingEntity entity, EquipmentSlot slot) {
+ return NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20) ? entity.canUseEquipmentSlot(slot) : super.canUseEquipmentSlot(entity, slot);
+ }
+
+ @Override
+ public boolean hasCustomName(PotionMeta meta) {
+ return meta.hasCustomPotionName();
+ }
+
+ @Override
+ public void setMaterialTags(Material type, Set tags) {
+ if (!NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21)) {
+ super.setMaterialTags(type, tags);
+ return;
+ }
+ BlockTagsSetter.INSTANCE.setTags(type, tags);
+ }
+
+ @Override
+ public void addLink(ServerLinks links, String display, URI uri) {
+ links.addLink(PaperModule.parseFormattedText(display, ChatColor.WHITE), uri);
+ }
+
+ @Override
+ public double[] getRecentTps() {
+ return Bukkit.getTPS();
+ }
+
+ @Override
+ public String getCopperGolemState(CopperGolem copperGolem) {
+ return copperGolem.getWeatheringState().name();
+ }
+
+ @Override
+ public void setCopperGolemState(ElementTag variant, CopperGolem copperGolem, Mechanism mechanism) {
+ if (mechanism.requireEnum(WeatheringCopperState.class)) {
+ copperGolem.setWeatheringState(variant.asEnum(WeatheringCopperState.class));
+ }
+ }
}
diff --git a/plugin/pom.xml b/plugin/pom.xml
index 53587ee87f..f1abe6f709 100644
--- a/plugin/pom.xml
+++ b/plugin/pom.xml
@@ -5,7 +5,7 @@
com.denizenscript
denizen
- 1.3.0-SNAPSHOT
+ 1.3.1-SNAPSHOT
Denizen
Scriptable Minecraft and Citizens2
@@ -16,13 +16,21 @@
CUSTOM
+
+
+
+ everything
+ https://maven.citizensnpcs.co/repo
+
+
+
org.spigotmc
spigot-api
- 1.20.4-R0.1-SNAPSHOT
+ 1.21.11-R0.2-SNAPSHOT
jar
provided
@@ -36,7 +44,7 @@
net.citizensnpcs
citizens-main
- 2.0.33-SNAPSHOT
+ 2.0.41-SNAPSHOT
jar
provided
@@ -59,6 +67,11 @@
system
${project.basedir}/lib/Vault.jar
+
+ net.kyori
+ adventure-nbt
+ 4.26.1
+
it.unimi.dsi
diff --git a/plugin/src/main/java/com/denizenscript/denizen/Denizen.java b/plugin/src/main/java/com/denizenscript/denizen/Denizen.java
index 5a68b96155..9060b805b9 100644
--- a/plugin/src/main/java/com/denizenscript/denizen/Denizen.java
+++ b/plugin/src/main/java/com/denizenscript/denizen/Denizen.java
@@ -5,6 +5,7 @@
import com.denizenscript.denizen.events.server.ServerPrestartScriptEvent;
import com.denizenscript.denizen.events.server.ServerStartScriptEvent;
import com.denizenscript.denizen.nms.NMSHandler;
+import com.denizenscript.denizen.nms.NMSVersion;
import com.denizenscript.denizen.nms.interfaces.FakeArrow;
import com.denizenscript.denizen.nms.interfaces.FakePlayer;
import com.denizenscript.denizen.nms.interfaces.ItemProjectile;
@@ -146,7 +147,10 @@ else if (javaVersion.startsWith("17")) {
Debug.log("Running on fully supported Java 17.");
}
else if (javaVersion.startsWith("18") || javaVersion.startsWith("19")) {
- getLogger().warning("Running unreliable future Java version. modern Minecraft versions are built for Java 17. Other Java versions are not guaranteed to function properly.");
+ getLogger().warning("Running unreliable Java version. modern Minecraft versions are built for Java 21 or 17. Other Java versions are not guaranteed to function properly.");
+ }
+ else if (javaVersion.startsWith("21")) {
+ Debug.log("Running on fully supported Java 21.");
}
else {
Debug.log("Running on unrecognized (future?) Java version. May or may not work.");
@@ -159,12 +163,26 @@ else if (javaVersion.startsWith("18") || javaVersion.startsWith("19")) {
startedSuccessful = false;
return;
}
- if (!NMSHandler.instance.isCorrectMappingsCode()) {
- getLogger().warning("-------------------------------------");
- getLogger().warning("This build of Denizen was built for a different Spigot revision! This may potentially cause issues."
- + " If you are experiencing trouble, update Denizen and Spigot both to latest builds!"
- + " If this message appears with both Denizen and Spigot fully up-to-date, contact the Denizen team (via GitHub, Spigot, or Discord) to request an update be built.");
- getLogger().warning("-------------------------------------");
+ try {
+ if (Class.forName("com.destroystokyo.paper.PaperConfig") != null) {
+ supportsPaper = true;
+ }
+ }
+ catch (ClassNotFoundException ex) {
+ // Ignore.
+ }
+ catch (Throwable ex) {
+ Debug.echoError(ex);
+ }
+ if (!NMSHandler.instance.isExactServerVersionMatch()) {
+ String serverSoftware = supportsPaper ? "Paper" : "Spigot";
+ getLogger().warning("""
+ \n-------------------------------------
+ This build of Denizen was built for a different Minecraft version! This may potentially cause issues.
+ If you are experiencing trouble, update Denizen and both to latest builds!
+ If this message appears with both Denizen and fully up-to-date, contact the Denizen team (via Discord) to request an update be built.
+ -------------------------------------""".replace("", serverSoftware)
+ );
}
triggerRegistry = new TriggerRegistry();
boolean citizensBork = false;
@@ -202,17 +220,6 @@ else if (javaVersion.startsWith("18") || javaVersion.startsWith("19")) {
catch (Exception e) {
Debug.echoError(e);
}
- try {
- if (Class.forName("com.destroystokyo.paper.PaperConfig") != null) {
- supportsPaper = true;
- }
- }
- catch (ClassNotFoundException ex) {
- // Ignore.
- }
- catch (Throwable ex) {
- Debug.echoError(ex);
- }
// bstats.org
try {
BStatsMetricsLite metrics = new BStatsMetricsLite(this);
@@ -290,6 +297,9 @@ else if (javaVersion.startsWith("18") || javaVersion.startsWith("19")) {
configOutput.close();
reloadConfig();
}
+ if (Settings.cache_legacySpigotNamesSupport) {
+ Debug.log("Legacy Spigot name support enabled. This may be unnecessary; see config.yml for more information.");
+ }
}
catch (Exception e) {
Debug.echoError(e);
@@ -338,7 +348,10 @@ else if (javaVersion.startsWith("18") || javaVersion.startsWith("19")) {
Debug.echoError(e);
}
try {
- new CommandEvents();
+ // TODO: temporary patch, should switch to custom click events
+ if (NMSHandler.getVersion().isAtMost(NMSVersion.v1_20)) {
+ new CommandEvents();
+ }
if (Settings.cache_packetInterceptAutoInit) {
NetworkInterceptHelper.enable();
}
@@ -611,4 +624,9 @@ public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
default -> null;
};
}
+
+ @Override
+ public File getFile() {
+ return super.getFile();
+ }
}
diff --git a/plugin/src/main/java/com/denizenscript/denizen/events/BukkitScriptEvent.java b/plugin/src/main/java/com/denizenscript/denizen/events/BukkitScriptEvent.java
index 601a52da4a..dd1a307ff7 100644
--- a/plugin/src/main/java/com/denizenscript/denizen/events/BukkitScriptEvent.java
+++ b/plugin/src/main/java/com/denizenscript/denizen/events/BukkitScriptEvent.java
@@ -8,8 +8,10 @@
import com.denizenscript.denizen.scripts.containers.core.ItemScriptHelper;
import com.denizenscript.denizen.tags.BukkitTagContext;
import com.denizenscript.denizen.utilities.NotedAreaTracker;
+import com.denizenscript.denizen.utilities.Utilities;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
import com.denizenscript.denizen.utilities.inventory.SlotHelper;
+import com.denizenscript.denizencore.events.ScriptEvent;
import com.denizenscript.denizencore.flags.AbstractFlagTracker;
import com.denizenscript.denizencore.flags.FlaggableObject;
import com.denizenscript.denizencore.objects.ObjectTag;
@@ -18,14 +20,13 @@
import com.denizenscript.denizencore.objects.notable.NoteManager;
import com.denizenscript.denizencore.tags.TagContext;
import com.denizenscript.denizencore.utilities.CoreConfiguration;
+import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.denizenscript.denizencore.utilities.Deprecations;
import com.denizenscript.denizencore.utilities.debugging.Debug;
-import com.denizenscript.denizencore.events.ScriptEvent;
-import com.denizenscript.denizencore.utilities.CoreUtilities;
-import org.bukkit.Bukkit;
-import org.bukkit.Location;
-import org.bukkit.Material;
-import org.bukkit.entity.*;
+import org.bukkit.*;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Vehicle;
import org.bukkit.event.*;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.plugin.EventExecutor;
@@ -85,6 +86,66 @@ public abstract class BukkitScriptEvent extends ScriptEvent {
//
// -->
+ // <--[extension]
+ // @name Advanced Object Matching Extension
+ // @target_type language
+ // @target_name Advanced Object Matching
+ // @description
+ // Object types have their own special supported matchable inputs, refer to <@link language Advanced Object Matchables>.
+ // -->
+
+ // <--[language]
+ // @name Script Event Switches
+ // @group Script Events
+ // @description
+ // Modern script events support the concept of 'switches'.
+ // A switch is a specification of additional requirements in an event line other than what's in the event label it.
+ //
+ // A switch consists of a name and a value input, and are can be added anywhere in an event line as "name:".
+ // For example, "on delta time secondly every:5:" is a valid event, where "delta time secondly" is the event itself, and "every:<#>" is a switch available to the event.
+ //
+ // A traditional Denizen event might look like "on damaged",
+ // where "" can be filled with "entity" or any entity type (like "player").
+ // A switch-using event would instead take the format "on entity damaged" with switch "type:"
+ // meaning you can do "on entity damaged" for any entity, or "on entity damaged type:player:" for players specifically.
+ // This is both more efficient to process and more explicit in what's going on, however it is less clear/readable to the average user, so it is not often used.
+ // Some events may have switches for less-often specified data, and use the event line for other options.
+ //
+ // There are also some standard switches available to every script event, and some available to an entire category of script events.
+ //
+ // One switch available to every event is "server_flagged:", which requires that there be a server flag under the given name.
+ // For example, "on console output server_flagged:recording:" will only run the handler for console output when the "recording" flag is set on the server.
+ // This can also be used to require the server does NOT have a flag with "server_flagged:!"
+ //
+ // "chance:" is also a globally available switch.
+ // For example, "on player breaks diamond_ore chance:25:" will only fire on average one in every four times that a player breaks a diamond ore block.
+ //
+ // Events that have a player linked have the "flagged" and "permission" switches available.
+ //
+ // If the switch is specified, and an event doesn't have a linked player, the event will automatically fail to match.
+ // The "flagged:" switch will limit the event to only fire when the player has the flag with the specified name.
+ // It can be used like "on player breaks block flagged:nobreak:" (that would be used alongside "- flag player nobreak").
+ // You can also use "flagged:!" to require the player does NOT have the flag, like "on player breaks block flagged:!griefbypass:"
+ //
+ // The "permission:" will limit the event to only fire when the player has the specified permission key.
+ // It can be used like "on player breaks block permission:denizen.my.perm:"
+ // For multiple flag or permission requirements, just list them separated by '|' pipes, like "flagged:a|b|c". This will require all named flags/permissions to be present, not just one.
+ //
+ // Events that have an NPC linked have the "assigned" switch available.
+ // If the switch is specified, and an event doesn't have a linked NPC, the event will automatically fail to match.
+ // The "assigned: