diff --git a/plugin/src/main/java/com/denizenscript/denizen/nms/interfaces/PacketHelper.java b/plugin/src/main/java/com/denizenscript/denizen/nms/interfaces/PacketHelper.java index 6a09c70a6e..85b22e3554 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/nms/interfaces/PacketHelper.java +++ b/plugin/src/main/java/com/denizenscript/denizen/nms/interfaces/PacketHelper.java @@ -141,4 +141,8 @@ default void sendBrand(Player player, String brand) { default void sendCollectItemEntity(Player player, Entity taker, Entity item, int amount) { throw new UnsupportedOperationException(); } + + default void sendRelativeLookPacket(Player player, float yaw, float pitch) { + throw new UnsupportedOperationException(); + } } diff --git a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/entity/HurtCommand.java b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/entity/HurtCommand.java index 103d4da65c..f6a0463c77 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/entity/HurtCommand.java +++ b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/entity/HurtCommand.java @@ -6,6 +6,7 @@ import com.denizenscript.denizencore.exceptions.InvalidArgumentsRuntimeException; import com.denizenscript.denizencore.objects.ObjectTag; import com.denizenscript.denizencore.scripts.commands.generator.*; +import com.denizenscript.denizencore.utilities.Deprecations; import com.denizenscript.denizencore.utilities.debugging.Debug; import com.denizenscript.denizen.objects.EntityTag; import com.denizenscript.denizencore.objects.core.ElementTag; @@ -82,6 +83,7 @@ public static void autoExecute(ScriptEntry scriptEntry, ObjectTag swapEntities = entitiesObj; entitiesObj = amountObj; if (swapEntities != null && swapEntities.asElement().isDouble()) { + Deprecations.outOfOrderArgs.warn(scriptEntry); amountObj = swapEntities; } else { diff --git a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/entity/LookCommand.java b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/entity/LookCommand.java index 5f55c40d53..04bcea6e5b 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/entity/LookCommand.java +++ b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/entity/LookCommand.java @@ -2,20 +2,26 @@ import com.denizenscript.denizen.Denizen; import com.denizenscript.denizen.utilities.PaperAPITools; -import com.denizenscript.denizen.utilities.Utilities; -import com.denizenscript.denizencore.utilities.debugging.Debug; +import com.denizenscript.denizen.utilities.packets.NetworkInterceptHelper; +import com.denizenscript.denizencore.DenizenCore; +import com.denizenscript.denizencore.objects.ObjectTag; +import com.denizenscript.denizencore.scripts.commands.generator.ArgDefaultNull; +import com.denizenscript.denizencore.scripts.commands.generator.ArgLinear; +import com.denizenscript.denizencore.scripts.commands.generator.ArgName; +import com.denizenscript.denizencore.scripts.commands.generator.ArgPrefixed; +import com.denizenscript.denizencore.utilities.Deprecations; import com.denizenscript.denizen.nms.NMSHandler; import com.denizenscript.denizen.objects.EntityTag; import com.denizenscript.denizen.objects.LocationTag; -import com.denizenscript.denizencore.exceptions.InvalidArgumentsException; import com.denizenscript.denizencore.exceptions.InvalidArgumentsRuntimeException; -import com.denizenscript.denizencore.objects.Argument; import com.denizenscript.denizencore.objects.core.DurationTag; import com.denizenscript.denizencore.objects.core.ElementTag; import com.denizenscript.denizencore.objects.core.ListTag; import com.denizenscript.denizencore.scripts.ScriptEntry; import com.denizenscript.denizencore.scripts.commands.AbstractCommand; +import com.denizenscript.denizencore.utilities.debugging.Debug; import org.bukkit.Location; +import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitTask; @@ -27,16 +33,18 @@ public class LookCommand extends AbstractCommand { public LookCommand() { setName("look"); - setSyntax("look (|...) [/cancel/yaw: pitch:] (duration:)"); - setRequiredArguments(1, 4); + setSyntax("look (|...) [/cancel/yaw: pitch:] (duration:) (offthread_repeat:<#>)"); + setRequiredArguments(1, 5); isProcedural = false; + addRemappedPrefixes("duration", "d"); + autoCompile(); } // <--[command] // @Name Look - // @Syntax look (|...) [/cancel/yaw: pitch:] (duration:) + // @Syntax look (|...) [/cancel/yaw: pitch:] (duration:) (offthread_repeat:<#>) // @Required 1 - // @Maximum 4 + // @Maximum 5 // @Short Causes the NPC or other entity to look at a target location. // @Synonyms Turn,Face // @Group entity @@ -51,6 +59,9 @@ public LookCommand() { // If a duration is set, the entity cannot look away from the location until the duration has expired. // Use the cancel argument to end the duration earlier. // + // Optionally, you can use the "offthread_repeat:" option alongside "yaw:" and "pitch:" + // to cause a player's rotation to be smoothed out with a specified number of extra async rotation packets within a single tick. + // // @Tags // // @@ -64,70 +75,25 @@ public LookCommand() { // - look duration:10s // --> - @Override - public void parseArgs(ScriptEntry scriptEntry) throws InvalidArgumentsException { - for (Argument arg : scriptEntry) { - if (!scriptEntry.hasObject("location") - && !scriptEntry.hasObject("cancel") - && arg.limitToOnlyPrefix("location") - && arg.matchesArgumentType(LocationTag.class)) { - scriptEntry.addObject("location", arg.asType(LocationTag.class)); - } - else if (!scriptEntry.hasObject("cancel") - && !scriptEntry.hasObject("location") - && arg.matches("cancel")) { - scriptEntry.addObject("cancel", new ElementTag("true")); - } - else if (!scriptEntry.hasObject("yaw") - && arg.matchesPrefix("yaw") - && arg.matchesFloat()) { - scriptEntry.addObject("yaw", arg.asElement()); - } - else if (!scriptEntry.hasObject("pitch") - && arg.matchesPrefix("pitch") - && arg.matchesFloat()) { - scriptEntry.addObject("pitch", arg.asElement()); - } - else if (!scriptEntry.hasObject("duration") - && arg.matchesArgumentType(DurationTag.class) - && arg.matchesPrefix("duration", "d")) { - scriptEntry.addObject("duration", arg.asType(DurationTag.class)); - } - else if (!scriptEntry.hasObject("entities") - && arg.matchesArgumentList(EntityTag.class)) { - scriptEntry.addObject("entities", arg.asType(ListTag.class).filter(EntityTag.class, scriptEntry)); - } - else { - arg.reportUnhandled(); - } - } - if (!scriptEntry.hasObject("entities")) { - scriptEntry.defaultObject("entities", Utilities.entryDefaultEntityList(scriptEntry, false)); - } - if (!scriptEntry.hasObject("location") && !scriptEntry.hasObject("cancel") && !scriptEntry.hasObject("yaw")) { - throw new InvalidArgumentsException("Must specify a location, a yaw and pitch, or 'cancel'!"); - } - if (!scriptEntry.hasObject("entities")) { - throw new InvalidArgumentsException("Must specify an entity!"); - } - } - public static HashMap lookTasks = new HashMap<>(); - @Override - public void execute(ScriptEntry scriptEntry) { - final LocationTag loc = scriptEntry.getObjectTag("location"); - List entities = (List) scriptEntry.getObject("entities"); - if (entities == null) { - throw new InvalidArgumentsRuntimeException("Missing entity target input"); - } - final DurationTag duration = scriptEntry.getObjectTag("duration"); - ElementTag yaw = scriptEntry.getElement("yaw"); - ElementTag pitch = scriptEntry.getElement("pitch"); - ElementTag cancel = scriptEntry.getElement("cancel"); - if (scriptEntry.dbCallShouldDebug()) { - Debug.report(scriptEntry, getName(), cancel, loc, duration, yaw, pitch, db("entities", entities)); + public static void autoExecute(ScriptEntry scriptEntry, + @ArgName("entities") @ArgDefaultNull @ArgLinear ObjectTag entitiesObj, + @ArgName("location") @ArgDefaultNull @ArgLinear ObjectTag locationObj, + @ArgName("duration") @ArgDefaultNull @ArgPrefixed DurationTag duration, + @ArgName("yaw") @ArgDefaultNull @ArgPrefixed ElementTag yaw, + @ArgName("pitch") @ArgDefaultNull @ArgPrefixed ElementTag pitch, + @ArgName("offthread_repeat") @ArgDefaultNull @ArgPrefixed ElementTag offthreadRepeats) { + if (!(entitiesObj instanceof ListTag)) { + String entStr = entitiesObj.asElement().asLowerString(); + if (entStr.equals("cancel") || entStr.startsWith("l@")) { + Deprecations.outOfOrderArgs.warn(scriptEntry); + ObjectTag swap = entitiesObj; + entitiesObj = locationObj; + locationObj = swap; + } } + List entities = entitiesObj.asType(ListTag.class, scriptEntry.context).filter(EntityTag.class, scriptEntry); for (EntityTag entity : entities) { if (entity.isSpawned()) { BukkitTask task = lookTasks.remove(entity.getUUID()); @@ -136,9 +102,13 @@ public void execute(ScriptEntry scriptEntry) { } } } - if (cancel != null && cancel.asBoolean()) { + if (locationObj != null && !(locationObj instanceof LocationTag) && locationObj.asElement().asLowerString().equals("cancel")) { return; } + LocationTag loc = locationObj == null ? null : locationObj.asType(LocationTag.class, scriptEntry.context); + if (loc == null && yaw == null && pitch == null) { + throw new InvalidArgumentsRuntimeException("Missing or invalid Location input!"); + } final float yawRaw = yaw == null ? 0 : yaw.asFloat(); final float pitchRaw = pitch == null ? 0 : pitch.asFloat(); for (EntityTag entity : entities) { @@ -149,9 +119,28 @@ public void execute(ScriptEntry scriptEntry) { else { if (entity.isPlayer()) { Location playerTeleDest = entity.getLocation().clone(); + float relYaw = yawRaw - playerTeleDest.getYaw(); + float relPitch = pitchRaw - playerTeleDest.getPitch(); playerTeleDest.setYaw(yawRaw); playerTeleDest.setPitch(pitchRaw); - PaperAPITools.instance.teleportPlayerRelative(entity.getPlayer(), playerTeleDest); + Player player = entity.getPlayer(); + PaperAPITools.instance.teleportPlayerRelative(player, playerTeleDest); + if (offthreadRepeats != null) { + NetworkInterceptHelper.enable(); + int times = offthreadRepeats.asInt(); + int ms = 50 / (times + 1); + DenizenCore.runAsync(() -> { + try { + for (int i = 0; i < times; i++) { + Thread.sleep(ms); + NMSHandler.packetHelper.sendRelativeLookPacket(player, relYaw, relPitch); + } + } + catch (Throwable ex) { + Debug.echoError(ex); + } + }); + } } else { NMSHandler.entityHelper.rotate(entity.getBukkitEntity(), yawRaw, pitchRaw); diff --git a/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/helpers/PacketHelperImpl.java b/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/helpers/PacketHelperImpl.java index 74cbc44904..97f44eb16c 100644 --- a/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/helpers/PacketHelperImpl.java +++ b/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/helpers/PacketHelperImpl.java @@ -440,6 +440,12 @@ public void sendCollectItemEntity(Player player, Entity taker, Entity item, int send(player, packet); } + @Override + public void sendRelativeLookPacket(Player player, float yaw, float pitch) { + ClientboundPlayerPositionPacket packet = new ClientboundPlayerPositionPacket(0, 0, 0, yaw, pitch, ClientboundPlayerPositionPacket.RelativeArgument.ALL, 0, false); + ((DenizenNetworkManagerImpl) ((CraftPlayer) player).getHandle().connection.connection).oldManager.channel.writeAndFlush(packet); + } + public static void send(Player player, Packet packet) { ((CraftPlayer) player).getHandle().connection.send(packet); }