diff --git a/README.md b/README.md index 0b78fa7..a081964 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Welcome to BetterModel! -### Modern lightweight Minecraft model implementation for Paper, Folia +### Modern lightweight Minecraft model implementation for Bukkit, Folia This plugin implements server-side model by using packet-based item display. - Importing .bbmodel @@ -55,7 +55,7 @@ partvisibility ``` ### Version -- Paper of that's fork (with Folia) +- Spigot of that's fork (with Folia) - Java 21 - Minecraft 1.20.4-1.21.4 @@ -65,6 +65,8 @@ No ### Command /bettermodel reload - Reloads this plugin. /bettermodel spawn - Summons this model to husk. +/bettermodel limb - Show/Hide player limb. +/bettermodel play - Plays player animation. ### Permission bettermodel - Accesses to command. diff --git a/api/src/main/java/kr/toxicity/model/api/BetterModel.java b/api/src/main/java/kr/toxicity/model/api/BetterModel.java index e1593ac..3c8018f 100644 --- a/api/src/main/java/kr/toxicity/model/api/BetterModel.java +++ b/api/src/main/java/kr/toxicity/model/api/BetterModel.java @@ -2,6 +2,7 @@ import kr.toxicity.model.api.manager.*; import kr.toxicity.model.api.nms.NMS; +import kr.toxicity.model.api.scheduler.ModelScheduler; import kr.toxicity.model.api.version.MinecraftVersion; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; @@ -10,8 +11,21 @@ public abstract class BetterModel extends JavaPlugin { + public static final boolean IS_PAPER; + private static BetterModel instance; + static { + boolean paper; + try { + Class.forName("io.papermc.paper.configuration.PaperConfigurations"); + paper = true; + } catch (Exception e) { + paper = false; + } + IS_PAPER = paper; + } + @Override public final void onLoad() { if (instance != null) throw new RuntimeException(); @@ -33,6 +47,7 @@ public final void onLoad() { public abstract @NotNull CommandManager commandManager(); public abstract @NotNull CompatibilityManager compatibilityManager(); public abstract @NotNull ConfigManager configManager(); + public abstract @NotNull ModelScheduler scheduler(); public sealed interface ReloadResult { record Success(long time) implements ReloadResult { diff --git a/api/src/main/java/kr/toxicity/model/api/event/ModelDamagedEvent.java b/api/src/main/java/kr/toxicity/model/api/event/ModelDamagedEvent.java index 0cda8cf..a390ab5 100644 --- a/api/src/main/java/kr/toxicity/model/api/event/ModelDamagedEvent.java +++ b/api/src/main/java/kr/toxicity/model/api/event/ModelDamagedEvent.java @@ -13,6 +13,7 @@ @Getter @Setter public class ModelDamagedEvent extends EntityEvent implements Cancellable { + public static final HandlerList HANDLER_LIST = new HandlerList(); private final @NotNull HitBox hitBox; diff --git a/api/src/main/java/kr/toxicity/model/api/nms/EntityAdapter.java b/api/src/main/java/kr/toxicity/model/api/nms/EntityAdapter.java index 9b55b92..8aa300f 100644 --- a/api/src/main/java/kr/toxicity/model/api/nms/EntityAdapter.java +++ b/api/src/main/java/kr/toxicity/model/api/nms/EntityAdapter.java @@ -20,6 +20,11 @@ public float bodyYaw() { return 0; } + @Override + public float pitch() { + return 0; + } + @Override public float yaw() { return 0; @@ -33,6 +38,7 @@ public float yaw() { boolean onWalk(); double scale(); + float pitch(); float bodyYaw(); float yaw(); @NotNull Vector3f passengerPosition(); diff --git a/api/src/main/java/kr/toxicity/model/api/scheduler/ModelScheduler.java b/api/src/main/java/kr/toxicity/model/api/scheduler/ModelScheduler.java new file mode 100644 index 0000000..7ddc10f --- /dev/null +++ b/api/src/main/java/kr/toxicity/model/api/scheduler/ModelScheduler.java @@ -0,0 +1,12 @@ +package kr.toxicity.model.api.scheduler; + +import org.bukkit.Location; +import org.jetbrains.annotations.NotNull; + +public interface ModelScheduler { + @NotNull ModelTask task(@NotNull Location location, @NotNull Runnable runnable); + @NotNull ModelTask taskLater(long delay, @NotNull Location location, @NotNull Runnable runnable); + @NotNull ModelTask asyncTask(@NotNull Runnable runnable); + @NotNull ModelTask asyncTaskLater(long delay, @NotNull Runnable runnable); + @NotNull ModelTask asyncTaskTimer(long delay, long period, @NotNull Runnable runnable); +} diff --git a/api/src/main/java/kr/toxicity/model/api/scheduler/ModelTask.java b/api/src/main/java/kr/toxicity/model/api/scheduler/ModelTask.java new file mode 100644 index 0000000..de79d70 --- /dev/null +++ b/api/src/main/java/kr/toxicity/model/api/scheduler/ModelTask.java @@ -0,0 +1,6 @@ +package kr.toxicity.model.api.scheduler; + +public interface ModelTask { + boolean isCancelled(); + void cancel(); +} diff --git a/api/src/main/java/kr/toxicity/model/api/tracker/EntityTracker.java b/api/src/main/java/kr/toxicity/model/api/tracker/EntityTracker.java index 5094cf0..6749f2c 100644 --- a/api/src/main/java/kr/toxicity/model/api/tracker/EntityTracker.java +++ b/api/src/main/java/kr/toxicity/model/api/tracker/EntityTracker.java @@ -11,7 +11,6 @@ import kr.toxicity.model.api.nms.PlayerChannelHandler; import kr.toxicity.model.api.util.EntityUtil; import lombok.Getter; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; @@ -67,7 +66,7 @@ public EntityTracker(@NotNull Entity entity, @NotNull RenderInstance instance) { r -> r.getName().startsWith("h_"), a -> { if (a.rotation() != null && !isRunningSingleAnimation()) { - a.rotation().add(-entity.getPitch(), Math.clamp( + a.rotation().add(-adapter.pitch(), Math.clamp( -adapter.yaw() + adapter.bodyYaw(), -45, 45 @@ -82,7 +81,7 @@ public EntityTracker(@NotNull Entity entity, @NotNull RenderInstance instance) { )); entity.getPersistentDataContainer().set(TRACKING_ID, PersistentDataType.STRING, instance.getParent().getParent().name()); TRACKER_MAP.put(entity.getUniqueId(), this); - Bukkit.getRegionScheduler().run(BetterModel.inst(), entity.getLocation(), s -> { + BetterModel.inst().scheduler().task(entity.getLocation(), () -> { if (!closed.get() && !forRemoval()) createHitBox(); }); instance.setup(); @@ -135,8 +134,9 @@ public void close() throws Exception { } public void spawnNearby(@NotNull Location location) { - for (Player nearbyPlayer : location.getNearbyPlayers(EntityUtil.RENDER_DISTANCE, instance.spawnFilter())) { - spawn(nearbyPlayer); + var filter = instance.spawnFilter(); + for (Entity e : location.getWorld().getNearbyEntities(location, EntityUtil.RENDER_DISTANCE , EntityUtil.RENDER_DISTANCE , EntityUtil.RENDER_DISTANCE)) { + if (e instanceof Player player && filter.test(player)) spawn(player); } } diff --git a/api/src/main/java/kr/toxicity/model/api/tracker/Tracker.java b/api/src/main/java/kr/toxicity/model/api/tracker/Tracker.java index abf32cf..ce34631 100644 --- a/api/src/main/java/kr/toxicity/model/api/tracker/Tracker.java +++ b/api/src/main/java/kr/toxicity/model/api/tracker/Tracker.java @@ -1,16 +1,14 @@ package kr.toxicity.model.api.tracker; -import io.papermc.paper.threadedregions.scheduler.ScheduledTask; import kr.toxicity.model.api.BetterModel; import kr.toxicity.model.api.data.renderer.AnimationModifier; import kr.toxicity.model.api.data.renderer.RenderInstance; import kr.toxicity.model.api.entity.RenderedEntity; import kr.toxicity.model.api.entity.TrackerMovement; import kr.toxicity.model.api.nms.PacketBundler; +import kr.toxicity.model.api.scheduler.ModelTask; import kr.toxicity.model.api.util.EntityUtil; import lombok.Getter; -import lombok.Setter; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; @@ -20,7 +18,6 @@ import java.util.List; import java.util.Objects; import java.util.UUID; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; import java.util.function.Supplier; @@ -29,7 +26,7 @@ public abstract class Tracker implements AutoCloseable { public static final NamespacedKey TRACKING_ID = Objects.requireNonNull(NamespacedKey.fromString("bettermodel_tracker")); protected final RenderInstance instance; - private final ScheduledTask task; + private final ModelTask task; private final AtomicBoolean runningSingle = new AtomicBoolean(); private TrackerMovement before; @@ -38,14 +35,14 @@ public abstract class Tracker implements AutoCloseable { private Supplier movement = () -> new TrackerMovement(new Vector3f(), new Vector3f(1), new Vector3f()); public Tracker(@NotNull RenderInstance instance) { this.instance = instance; - task = Bukkit.getAsyncScheduler().runAtFixedRate(BetterModel.inst(), task -> { + task = BetterModel.inst().scheduler().asyncTaskTimer(1, 1, () -> { if (viewedPlayerSize() == 0) return; var bundle = BetterModel.inst().nms().createBundler(); instance.move(isRunningSingleAnimation() && before != null && BetterModel.inst().configManager().lockOnPlayAnimation() ? before : (before = movement.get()), bundle); if (!bundle.isEmpty()) for (Player player : instance.viewedPlayer()) { bundle.send(player); } - }, 50, 50, TimeUnit.MILLISECONDS); + }); tint(false); instance.filter(p -> EntityUtil.canSee(p.getLocation(), location())); } diff --git a/build.gradle.kts b/build.gradle.kts index e0a92d7..699c8d0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,7 +16,7 @@ allprojects { apply(plugin = "java") apply(plugin = "kotlin") group = "kr.toxicity.model" - version = "1.1" + version = "1.2" repositories { mavenCentral() maven("https://repo.papermc.io/repository/maven-public/") @@ -125,7 +125,9 @@ bukkitPluginYaml { description = "Accesses to command." children = mapOf( "reload" to true, - "spawn" to true + "spawn" to true, + "play" to true, + "limb" to true ) } } \ No newline at end of file diff --git a/core/src/main/kotlin/kr/toxicity/model/BetterModelImpl.kt b/core/src/main/kotlin/kr/toxicity/model/BetterModelImpl.kt index 3692af3..fbca778 100644 --- a/core/src/main/kotlin/kr/toxicity/model/BetterModelImpl.kt +++ b/core/src/main/kotlin/kr/toxicity/model/BetterModelImpl.kt @@ -4,9 +4,12 @@ import kr.toxicity.model.api.BetterModel import kr.toxicity.model.api.BetterModel.ReloadResult.* import kr.toxicity.model.api.manager.* import kr.toxicity.model.api.nms.NMS +import kr.toxicity.model.api.scheduler.ModelScheduler import kr.toxicity.model.api.version.MinecraftVersion import kr.toxicity.model.api.version.MinecraftVersion.* import kr.toxicity.model.manager.* +import kr.toxicity.model.scheduler.PaperScheduler +import kr.toxicity.model.scheduler.StandardScheduler import kr.toxicity.model.util.warn import org.bukkit.Bukkit import java.io.InputStream @@ -31,6 +34,8 @@ class BetterModelImpl : BetterModel() { ) } + private val scheduler = if (IS_PAPER) PaperScheduler() else StandardScheduler() + override fun onEnable() { nms = when (version) { V1_21_4 -> kr.toxicity.model.nms.v1_21_R3.NMSImpl() @@ -48,7 +53,7 @@ class BetterModelImpl : BetterModel() { } } managers.forEach(GlobalManagerImpl::start) - Bukkit.getAsyncScheduler().runNow(this) { + scheduler.asyncTask { reload() } } @@ -84,7 +89,7 @@ class BetterModelImpl : BetterModel() { } } - + override fun scheduler(): ModelScheduler = scheduler override fun modelManager(): ModelManager = ModelManagerImpl override fun playerManager(): PlayerManager = PlayerManagerImpl override fun entityManager(): EntityManager = EntityManagerImpl diff --git a/core/src/main/kotlin/kr/toxicity/model/manager/CommandManagerImpl.kt b/core/src/main/kotlin/kr/toxicity/model/manager/CommandManagerImpl.kt index 6a2a471..f6aa62a 100644 --- a/core/src/main/kotlin/kr/toxicity/model/manager/CommandManagerImpl.kt +++ b/core/src/main/kotlin/kr/toxicity/model/manager/CommandManagerImpl.kt @@ -8,12 +8,9 @@ import dev.jorel.commandapi.arguments.BooleanArgument import dev.jorel.commandapi.arguments.StringArgument import dev.jorel.commandapi.executors.CommandExecutionInfo import dev.jorel.commandapi.executors.PlayerCommandExecutor -import kr.toxicity.model.api.BetterModel import kr.toxicity.model.api.BetterModel.ReloadResult.* import kr.toxicity.model.api.manager.CommandManager -import kr.toxicity.model.api.util.EntityUtil import kr.toxicity.model.util.PLUGIN -import org.bukkit.Bukkit import org.bukkit.entity.EntityType import org.bukkit.entity.Player import java.util.concurrent.CompletableFuture @@ -47,7 +44,7 @@ object CommandManagerImpl : CommandManager, GlobalManagerImpl { .withAliases("re", "rl") .withPermission("bettermodel.reload") .executes(CommandExecutionInfo { - Bukkit.getAsyncScheduler().runNow(BetterModel.inst()) { _ -> + PLUGIN.scheduler().asyncTask { when (val result = PLUGIN.reload()) { is OnReload -> it.sender().sendMessage("The plugin still on reload!") is Success -> it.sender().sendMessage("Reload completed (${result.time} time)") diff --git a/core/src/main/kotlin/kr/toxicity/model/manager/EntityManagerImpl.kt b/core/src/main/kotlin/kr/toxicity/model/manager/EntityManagerImpl.kt index 2e3ffc6..f9050b5 100644 --- a/core/src/main/kotlin/kr/toxicity/model/manager/EntityManagerImpl.kt +++ b/core/src/main/kotlin/kr/toxicity/model/manager/EntityManagerImpl.kt @@ -2,24 +2,49 @@ package kr.toxicity.model.manager import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent +import kr.toxicity.model.api.BetterModel import kr.toxicity.model.api.data.renderer.AnimationModifier import kr.toxicity.model.api.event.ModelInteractEvent import kr.toxicity.model.api.manager.EntityManager import kr.toxicity.model.api.tracker.EntityTracker import kr.toxicity.model.util.PLUGIN import kr.toxicity.model.util.registerListener -import org.bukkit.Bukkit import org.bukkit.entity.LivingEntity import org.bukkit.event.EventHandler import org.bukkit.event.Listener import org.bukkit.event.entity.EntityDamageEvent import org.bukkit.event.entity.EntityDeathEvent +import org.bukkit.event.entity.EntitySpawnEvent import org.bukkit.event.player.PlayerQuitEvent import org.bukkit.event.world.ChunkLoadEvent -import java.util.concurrent.TimeUnit object EntityManagerImpl : EntityManager, GlobalManagerImpl { override fun reload() { + if (BetterModel.IS_PAPER) registerListener(object : Listener { + @EventHandler + fun EntityRemoveFromWorldEvent.remove() { + EntityTracker.tracker(entity)?.let { + if (!it.forRemoval()) it.close() + } + } + @EventHandler + fun EntityAddToWorldEvent.add() { + EntityTracker.tracker(entity)?.refresh() + } + }) else registerListener(object : Listener { + @EventHandler + @Suppress("removal", "DEPRECATION") + fun org.bukkit.event.entity.EntityRemoveEvent.remove() { + EntityTracker.tracker(entity)?.let { + if (!it.forRemoval()) it.close() + } + } + @EventHandler + fun EntitySpawnEvent.add() { + EntityTracker.tracker(entity)?.refresh() + } + }) + registerListener(object : Listener { @EventHandler fun ModelInteractEvent.interact() { @@ -32,26 +57,16 @@ object EntityManagerImpl : EntityManager, GlobalManagerImpl { EntityTracker.tracker(player.uniqueId)?.close() } @EventHandler - fun EntityRemoveFromWorldEvent.remove() { - EntityTracker.tracker(entity)?.let { - if (!it.forRemoval()) it.close() - } - } - @EventHandler fun ChunkLoadEvent.load() { chunk.entities.forEach { EntityTracker.tracker(it)?.refresh() } } @EventHandler - fun EntityAddToWorldEvent.add() { - EntityTracker.tracker(entity)?.refresh() - } - @EventHandler fun EntityDeathEvent.death() { EntityTracker.tracker(entity)?.let { if (!it.animateSingle("death", AnimationModifier.DEFAULT) { - Bukkit.getRegionScheduler().run(PLUGIN, entity.location) { _ -> + PLUGIN.scheduler().task(entity.location) { it.close() } }) it.close() @@ -67,9 +82,9 @@ object EntityManagerImpl : EntityManager, GlobalManagerImpl { }) it.tint(true) else { it.tint(true) - Bukkit.getAsyncScheduler().runDelayed(PLUGIN, { _ -> + PLUGIN.scheduler().asyncTaskLater(e.maximumNoDamageTicks.toLong() / 2) { it.tint(false) - }, e.maximumNoDamageTicks.toLong() * 25 , TimeUnit.MILLISECONDS) + } } } } diff --git a/core/src/main/kotlin/kr/toxicity/model/manager/PlayerManagerImpl.kt b/core/src/main/kotlin/kr/toxicity/model/manager/PlayerManagerImpl.kt index 2ae916d..d32c89e 100644 --- a/core/src/main/kotlin/kr/toxicity/model/manager/PlayerManagerImpl.kt +++ b/core/src/main/kotlin/kr/toxicity/model/manager/PlayerManagerImpl.kt @@ -49,11 +49,11 @@ object PlayerManagerImpl : PlayerManager, GlobalManagerImpl { private fun Player.showAll() { val loc = location - Bukkit.getRegionScheduler().runDelayed(PLUGIN, loc, { - loc.getNearbyLivingEntities(EntityUtil.RENDER_DISTANCE).forEach { + PLUGIN.scheduler().taskLater(10, loc) { + loc.world.getNearbyEntities(loc, EntityUtil.RENDER_DISTANCE, EntityUtil.RENDER_DISTANCE, EntityUtil.RENDER_DISTANCE).forEach { EntityTracker.tracker(it)?.spawn(this) } - }, 10) + } } private fun Player.register() = playerMap.computeIfAbsent(uniqueId) { diff --git a/core/src/main/kotlin/kr/toxicity/model/scheduler/PaperScheduler.kt b/core/src/main/kotlin/kr/toxicity/model/scheduler/PaperScheduler.kt new file mode 100644 index 0000000..9d87e84 --- /dev/null +++ b/core/src/main/kotlin/kr/toxicity/model/scheduler/PaperScheduler.kt @@ -0,0 +1,39 @@ +package kr.toxicity.model.scheduler + +import io.papermc.paper.threadedregions.scheduler.ScheduledTask +import kr.toxicity.model.api.scheduler.ModelScheduler +import kr.toxicity.model.api.scheduler.ModelTask +import kr.toxicity.model.util.PLUGIN +import org.bukkit.Bukkit +import org.bukkit.Location +import java.util.concurrent.TimeUnit + +class PaperScheduler : ModelScheduler { + + private fun ScheduledTask.wrap() = object : ModelTask { + override fun isCancelled(): Boolean = this@wrap.isCancelled + override fun cancel() { + this@wrap.cancel() + } + } + + override fun task(location: Location, runnable: Runnable) = Bukkit.getRegionScheduler().run(PLUGIN, location) { + runnable.run() + }.wrap() + + override fun taskLater(delay: Long, location: Location, runnable: Runnable) = Bukkit.getRegionScheduler().runDelayed(PLUGIN, location, { + runnable.run() + }, delay).wrap() + + override fun asyncTask(runnable: Runnable) = Bukkit.getAsyncScheduler().runNow(PLUGIN) { + runnable.run() + }.wrap() + + override fun asyncTaskLater(delay: Long, runnable: Runnable) = Bukkit.getAsyncScheduler().runDelayed(PLUGIN, { + runnable.run() + }, delay * 50, TimeUnit.MILLISECONDS).wrap() + + override fun asyncTaskTimer(delay: Long, period: Long, runnable: Runnable) = Bukkit.getAsyncScheduler().runAtFixedRate(PLUGIN, { + runnable.run() + }, delay * 50, period * 50, TimeUnit.MILLISECONDS).wrap() +} \ No newline at end of file diff --git a/core/src/main/kotlin/kr/toxicity/model/scheduler/StandardScheduler.kt b/core/src/main/kotlin/kr/toxicity/model/scheduler/StandardScheduler.kt new file mode 100644 index 0000000..999d879 --- /dev/null +++ b/core/src/main/kotlin/kr/toxicity/model/scheduler/StandardScheduler.kt @@ -0,0 +1,24 @@ +package kr.toxicity.model.scheduler + +import kr.toxicity.model.api.scheduler.ModelScheduler +import kr.toxicity.model.api.scheduler.ModelTask +import kr.toxicity.model.util.PLUGIN +import org.bukkit.Bukkit +import org.bukkit.Location +import org.bukkit.scheduler.BukkitTask + +class StandardScheduler : ModelScheduler { + + private fun BukkitTask.wrap() = object : ModelTask { + override fun isCancelled(): Boolean = this@wrap.isCancelled + override fun cancel() { + this@wrap.cancel() + } + } + + override fun task(location: Location, runnable: Runnable) = Bukkit.getScheduler().runTask(PLUGIN, runnable).wrap() + override fun taskLater(delay: Long, location: Location, runnable: Runnable) = Bukkit.getScheduler().runTaskLater(PLUGIN, runnable, delay).wrap() + override fun asyncTask(runnable: Runnable) = Bukkit.getScheduler().runTaskAsynchronously(PLUGIN, runnable).wrap() + override fun asyncTaskLater(delay: Long, runnable: Runnable) = Bukkit.getScheduler().runTaskLaterAsynchronously(PLUGIN, runnable, delay).wrap() + override fun asyncTaskTimer(delay: Long, period: Long, runnable: Runnable) = Bukkit.getScheduler().runTaskTimerAsynchronously(PLUGIN, runnable, delay, period).wrap() +} \ No newline at end of file diff --git a/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/Functions.kt b/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/Functions.kt index b428da8..eaf9f63 100644 --- a/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/Functions.kt +++ b/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/Functions.kt @@ -1,7 +1,12 @@ package kr.toxicity.model.nms.v1_20_R3 import kr.toxicity.model.api.data.blueprint.ModelBoundingBox +import net.minecraft.network.syncher.SynchedEntityData +import net.minecraft.network.syncher.SynchedEntityData.DataItem import net.minecraft.world.entity.Entity +import org.bukkit.Bukkit +import org.bukkit.event.Cancellable +import org.bukkit.event.Event import org.joml.Vector3f operator fun ModelBoundingBox.times(scale: Double) = ModelBoundingBox( @@ -15,4 +20,24 @@ operator fun ModelBoundingBox.times(scale: Double) = ModelBoundingBox( fun Entity.passengerPosition(): Vector3f { return Vector3f(0F, getDimensions(pose).height, 0F) +} + +fun Event.call(): Boolean { + Bukkit.getPluginManager().callEvent(this) + return if (this is Cancellable) !isCancelled else true +} + +private val DATA_ITEMS = SynchedEntityData::class.java.declaredFields.first { + it.type.isArray +}.apply { + isAccessible = true +} + +@Suppress("UNCHECKED_CAST") +fun SynchedEntityData.pack(): List> { + val list = arrayListOf>() + (DATA_ITEMS[this] as Array>).forEach { + list += it.value() + } + return list } \ No newline at end of file diff --git a/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/HitBoxImpl.kt b/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/HitBoxImpl.kt index ef34a81..ab64318 100644 --- a/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/HitBoxImpl.kt +++ b/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/HitBoxImpl.kt @@ -8,7 +8,8 @@ import kr.toxicity.model.api.nms.HitBox import kr.toxicity.model.api.nms.HitBoxListener import kr.toxicity.model.api.nms.TransformSupplier import net.minecraft.world.InteractionHand -import net.minecraft.world.InteractionHand.* +import net.minecraft.world.InteractionHand.MAIN_HAND +import net.minecraft.world.InteractionHand.OFF_HAND import net.minecraft.world.InteractionResult import net.minecraft.world.damagesource.DamageSource import net.minecraft.world.entity.* @@ -47,7 +48,7 @@ class HitBoxImpl( } override fun name(): String = name - override fun source(): Entity = delegate.bukkitLivingEntity + override fun source(): Entity = delegate.bukkitEntity override fun relativePosition(): Vector3f = position().run { Vector3f(x.toFloat(), y.toFloat(), z.toFloat()) } @@ -126,14 +127,14 @@ class HitBoxImpl( MAIN_HAND -> Hand.RIGHT OFF_HAND -> Hand.LEFT }) - if (!interact.callEvent()) return InteractionResult.FAIL + if (!interact.call()) return InteractionResult.FAIL return delegate.interact(player, hand) } override fun hurt(source: DamageSource, amount: Float): Boolean { val ds = CraftDamageSource(source) val event = ModelDamagedEvent(this, ds, amount) - if (!event.callEvent()) return false + if (!event.call()) return false if (listener.damage(ds, amount.toDouble())) return false return delegate.hurt(source, event.damage) } diff --git a/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/NMSImpl.kt b/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/NMSImpl.kt index 23051d7..b00504d 100644 --- a/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/NMSImpl.kt +++ b/nms/v1_20_R3/src/main/kotlin/kr/toxicity/model/nms/v1_20_R3/NMSImpl.kt @@ -14,8 +14,8 @@ import net.minecraft.network.Connection import net.minecraft.network.protocol.Packet import net.minecraft.network.protocol.game.* import net.minecraft.network.syncher.EntityDataAccessor -import net.minecraft.network.syncher.SynchedEntityData import net.minecraft.server.MinecraftServer +import net.minecraft.server.network.ServerCommonPacketListenerImpl import net.minecraft.world.entity.Display import net.minecraft.world.entity.Display.ItemDisplay import net.minecraft.world.entity.Entity @@ -25,7 +25,6 @@ import net.minecraft.world.entity.ai.attributes.Attributes import net.minecraft.world.entity.monster.Slime import net.minecraft.world.item.ItemDisplayContext import net.minecraft.world.item.Items -import org.bukkit.Bukkit import org.bukkit.Color import org.bukkit.Location import org.bukkit.craftbukkit.v1_20_R3.CraftWorld @@ -52,6 +51,27 @@ class NMSImpl : NMS { companion object { private const val INJECT_NAME = "bettermodel_channel_handler" + //Spigot + private val getConnection: (ServerCommonPacketListenerImpl) -> Connection = if (BetterModel.IS_PAPER) { + { + it.connection + } + } else { + ServerCommonPacketListenerImpl::class.java.declaredFields.first { f -> + f.type == Connection::class.java + }.apply { + isAccessible = true + }.let { get -> + { + get[it] as Connection + } + } + } + private fun Int.toEntity() = MinecraftServer.getServer().allLevels.firstNotNullOfOrNull { + it.entityLookup[this] + } + //Spigot + @Suppress("UNCHECKED_CAST") private val slimeSize = Slime::class.java.declaredFields.first { EntityDataAccessor::class.java.isAssignableFrom(it.type) @@ -83,10 +103,6 @@ class NMSImpl : NMS { } } - private fun Int.toEntity() = MinecraftServer.getServer().allLevels.firstNotNullOfOrNull { - it.entityLookup.get(this) - } - private fun Entity.toVoid(bundler: PacketBundlerImpl) { bundler.add(ClientboundSetEquipmentPacket(id, EquipmentSlot.entries.map { e -> Pair.of(e, Items.AIR.defaultInstance) @@ -95,7 +111,7 @@ class NMSImpl : NMS { isInvisible = true bundler.add(ClientboundSetEntityDataPacket( id, - entityData.nonDefaultValues!! + entityData.pack() )) isInvisible = inv } @@ -120,7 +136,7 @@ class NMSImpl : NMS { } override fun close() { - val channel = connection.connection.channel + val channel = getConnection(connection).channel channel.eventLoop().submit { channel.pipeline().remove(INJECT_NAME) } @@ -154,9 +170,10 @@ class NMSImpl : NMS { if (e is LivingEntity) { e.equipment?.let { i -> send(ClientboundSetEquipmentPacket(handle.id, org.bukkit.inventory.EquipmentSlot.entries.mapNotNull { - runCatching { - it to i.getItem(it) - }.getOrDefault(null) + val g = runCatching { + i.getItem(it) + }.getOrDefault(null) ?: return@mapNotNull null + it to g }.map { (type, item) -> Pair.of(when (type) { HAND -> EquipmentSlot.MAINHAND @@ -176,7 +193,7 @@ class NMSImpl : NMS { when (msg) { is ClientboundAddEntityPacket -> { msg.id.toEntity()?.let { e -> - Bukkit.getRegionScheduler().run(BetterModel.inst(), e.bukkitEntity.location) { + BetterModel.inst().scheduler().task(e.bukkitEntity.location) { EntityTracker.tracker(e.bukkitEntity)?.spawn(player) } } @@ -340,12 +357,7 @@ class NMSImpl : NMS { private val dataPacket get(): ClientboundSetEntityDataPacket { val set = if (itemChanged) transformSetWithItem else transformSet - val list = arrayListOf>() - display.entityData.packDirty()?.let(list::addAll) - display.entityData.nonDefaultValues?.let(list::addAll) - val result = ClientboundSetEntityDataPacket(display.id, list.distinctBy { - it.id - }.filter { + val result = ClientboundSetEntityDataPacket(display.id, display.entityData.pack().filter { set.contains(it.id) }) itemChanged = false @@ -402,7 +414,7 @@ class NMSImpl : NMS { override fun version(): NMSVersion = NMSVersion.V1_20_R3 - override fun adapt(entity: org.bukkit.entity.LivingEntity): EntityAdapter { + override fun adapt(entity: LivingEntity): EntityAdapter { val handle = (entity as CraftLivingEntity).handle return object : EntityAdapter { override fun onWalk(): Boolean { @@ -416,6 +428,10 @@ class NMSImpl : NMS { return 1.0 } + override fun pitch(): Float { + return handle.xRot + } + override fun bodyYaw(): Float { return handle.visualRotationYInDegrees } diff --git a/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/Functions.kt b/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/Functions.kt index ef88212..83322ba 100644 --- a/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/Functions.kt +++ b/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/Functions.kt @@ -1,8 +1,14 @@ package kr.toxicity.model.nms.v1_20_R4 +import kr.toxicity.model.api.BetterModel import kr.toxicity.model.api.data.blueprint.ModelBoundingBox +import net.minecraft.network.syncher.SynchedEntityData +import net.minecraft.network.syncher.SynchedEntityData.DataItem import net.minecraft.world.entity.Entity import net.minecraft.world.entity.EntityAttachment +import org.bukkit.Bukkit +import org.bukkit.event.Cancellable +import org.bukkit.event.Event import org.joml.Vector3f operator fun ModelBoundingBox.times(scale: Double) = ModelBoundingBox( @@ -18,4 +24,25 @@ fun Entity.passengerPosition(): Vector3f { return attachments.get(EntityAttachment.PASSENGER, 0, yRot).let { v -> Vector3f(v.x.toFloat(), v.y.toFloat(), v.z.toFloat()) } +} + +fun Event.call(): Boolean { + Bukkit.getPluginManager().callEvent(this) + return if (this is Cancellable) !isCancelled else true +} + +private val DATA_ITEMS = SynchedEntityData::class.java.declaredFields.first { + it.type.isArray +}.apply { + isAccessible = true +} + +@Suppress("UNCHECKED_CAST") +fun SynchedEntityData.pack(): List> { + if (BetterModel.IS_PAPER) return packAll()!! + val list = arrayListOf>() + (DATA_ITEMS[this] as Array>).forEach { + list += it.value() + } + return list } \ No newline at end of file diff --git a/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/HitBoxImpl.kt b/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/HitBoxImpl.kt index e95728a..14fc459 100644 --- a/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/HitBoxImpl.kt +++ b/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/HitBoxImpl.kt @@ -8,7 +8,8 @@ import kr.toxicity.model.api.nms.HitBox import kr.toxicity.model.api.nms.HitBoxListener import kr.toxicity.model.api.nms.TransformSupplier import net.minecraft.world.InteractionHand -import net.minecraft.world.InteractionHand.* +import net.minecraft.world.InteractionHand.MAIN_HAND +import net.minecraft.world.InteractionHand.OFF_HAND import net.minecraft.world.InteractionResult import net.minecraft.world.damagesource.DamageSource import net.minecraft.world.entity.* @@ -48,7 +49,7 @@ class HitBoxImpl( } override fun name(): String = name - override fun source(): Entity = delegate.bukkitLivingEntity + override fun source(): Entity = delegate.bukkitEntity override fun relativePosition(): Vector3f = position().run { Vector3f(x.toFloat(), y.toFloat(), z.toFloat()) } @@ -129,14 +130,14 @@ class HitBoxImpl( MAIN_HAND -> Hand.RIGHT OFF_HAND -> Hand.LEFT }) - if (!interact.callEvent()) return InteractionResult.FAIL + if (!interact.call()) return InteractionResult.FAIL return delegate.interact(player, hand) } override fun hurt(source: DamageSource, amount: Float): Boolean { val ds = CraftDamageSource(source) val event = ModelDamagedEvent(this, ds, amount) - if (!event.callEvent()) return false + if (!event.call()) return false if (listener.damage(ds, amount.toDouble())) return false return delegate.hurt(source, event.damage) } diff --git a/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/NMSImpl.kt b/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/NMSImpl.kt index f58625b..5504f7e 100644 --- a/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/NMSImpl.kt +++ b/nms/v1_20_R4/src/main/kotlin/kr/toxicity/model/nms/v1_20_R4/NMSImpl.kt @@ -15,12 +15,15 @@ import net.minecraft.network.protocol.Packet import net.minecraft.network.protocol.game.* import net.minecraft.network.syncher.EntityDataAccessor import net.minecraft.server.MinecraftServer -import net.minecraft.world.entity.* +import net.minecraft.server.network.ServerCommonPacketListenerImpl +import net.minecraft.world.entity.Display import net.minecraft.world.entity.Display.ItemDisplay +import net.minecraft.world.entity.Entity +import net.minecraft.world.entity.EntityType +import net.minecraft.world.entity.EquipmentSlot import net.minecraft.world.entity.ai.attributes.Attributes import net.minecraft.world.item.ItemDisplayContext import net.minecraft.world.item.Items -import org.bukkit.Bukkit import org.bukkit.Color import org.bukkit.Location import org.bukkit.craftbukkit.CraftWorld @@ -45,6 +48,26 @@ class NMSImpl : NMS { companion object { private const val INJECT_NAME = "bettermodel_channel_handler" + //Spigot + private val getConnection: (ServerCommonPacketListenerImpl) -> Connection = if (BetterModel.IS_PAPER) { + { + it.connection + } + } else { + ServerCommonPacketListenerImpl::class.java.declaredFields.first { f -> + f.type == Connection::class.java + }.apply { + isAccessible = true + }.let { get -> + { + get[it] as Connection + } + } + } + private fun Int.toEntity() = MinecraftServer.getServer().allLevels.firstNotNullOfOrNull { + it.entityLookup[this] + } + //Spigot private fun Class<*>.serializers() = declaredFields.filter { f -> EntityDataAccessor::class.java.isAssignableFrom(f.type) @@ -69,10 +92,6 @@ class NMSImpl : NMS { } } - private fun Int.toEntity() = MinecraftServer.getServer().allLevels.firstNotNullOfOrNull { - it.entityLookup.get(this) - } - private fun Entity.toVoid(bundler: PacketBundlerImpl) { bundler.add(ClientboundSetEquipmentPacket(id, EquipmentSlot.entries.map { e -> Pair.of(e, Items.AIR.defaultInstance) @@ -93,7 +112,7 @@ class NMSImpl : NMS { private val entityUUIDMap = ConcurrentHashMap() init { - val pipeLine = connection.connection.channel.pipeline() + val pipeLine = getConnection(connection).channel.pipeline() pipeLine.toMap().forEach { if (it.value is Connection) pipeLine.addBefore(it.key, INJECT_NAME, this) } @@ -136,13 +155,14 @@ class NMSImpl : NMS { val e = tracker.entity val handle = (e as CraftEntity).handle entityUUIDMap.remove(handle.uuid) - send(ClientboundSetEntityDataPacket(handle.id, handle.entityData.packAll()!!)) + send(ClientboundSetEntityDataPacket(handle.id, handle.entityData.pack())) if (e is LivingEntity) { e.equipment?.let { i -> send(ClientboundSetEquipmentPacket(handle.id, org.bukkit.inventory.EquipmentSlot.entries.mapNotNull { - runCatching { - it to i.getItem(it) - }.getOrDefault(null) + val g = runCatching { + i.getItem(it) + }.getOrDefault(null) ?: return@mapNotNull null + it to g }.map { (type, item) -> Pair.of(when (type) { HAND -> EquipmentSlot.MAINHAND @@ -163,7 +183,7 @@ class NMSImpl : NMS { when (msg) { is ClientboundAddEntityPacket -> { msg.id.toEntity()?.let { e -> - Bukkit.getRegionScheduler().run(BetterModel.inst(), e.bukkitEntity.location) { + BetterModel.inst().scheduler().task(e.bukkitEntity.location) { EntityTracker.tracker(e.bukkitEntity)?.spawn(player) } } @@ -267,7 +287,7 @@ class NMSImpl : NMS { bundler.unwrap().add(addPacket) val f = display.transformationInterpolationDuration frame(0) - bundler.unwrap().add(ClientboundSetEntityDataPacket(display.id, display.entityData.packAll()!!)) + bundler.unwrap().add(ClientboundSetEntityDataPacket(display.id, display.entityData.pack())) frame(f) } @@ -327,7 +347,7 @@ class NMSImpl : NMS { private val dataPacket get(): ClientboundSetEntityDataPacket { val set = if (itemChanged) transformSetWithItem else transformSet - val result = ClientboundSetEntityDataPacket(display.id, display.entityData.packAll()!!.filter { + val result = ClientboundSetEntityDataPacket(display.id, display.entityData.pack().filter { set.contains(it.id) }) itemChanged = false @@ -384,7 +404,7 @@ class NMSImpl : NMS { override fun version(): NMSVersion = NMSVersion.V1_20_R4 - override fun adapt(entity: org.bukkit.entity.LivingEntity): EntityAdapter { + override fun adapt(entity: LivingEntity): EntityAdapter { val handle = (entity as CraftLivingEntity).handle return object : EntityAdapter { override fun onWalk(): Boolean { @@ -398,6 +418,10 @@ class NMSImpl : NMS { return handle.attributes.getInstance(Attributes.SCALE)?.value ?: 1.0 } + override fun pitch(): Float { + return handle.xRot + } + override fun bodyYaw(): Float { return handle.visualRotationYInDegrees } diff --git a/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/Functions.kt b/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/Functions.kt index 5e2e850..fd103eb 100644 --- a/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/Functions.kt +++ b/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/Functions.kt @@ -1,8 +1,14 @@ package kr.toxicity.model.nms.v1_21_R1 +import kr.toxicity.model.api.BetterModel import kr.toxicity.model.api.data.blueprint.ModelBoundingBox +import net.minecraft.network.syncher.SynchedEntityData +import net.minecraft.network.syncher.SynchedEntityData.DataItem import net.minecraft.world.entity.Entity import net.minecraft.world.entity.EntityAttachment +import org.bukkit.Bukkit +import org.bukkit.event.Cancellable +import org.bukkit.event.Event import org.joml.Vector3f operator fun ModelBoundingBox.times(scale: Double) = ModelBoundingBox( @@ -18,4 +24,25 @@ fun Entity.passengerPosition(): Vector3f { return attachments.get(EntityAttachment.PASSENGER, 0, yRot).let { v -> Vector3f(v.x.toFloat(), v.y.toFloat(), v.z.toFloat()) } +} + +fun Event.call(): Boolean { + Bukkit.getPluginManager().callEvent(this) + return if (this is Cancellable) !isCancelled else true +} + +private val DATA_ITEMS = SynchedEntityData::class.java.declaredFields.first { + it.type.isArray +}.apply { + isAccessible = true +} + +@Suppress("UNCHECKED_CAST") +fun SynchedEntityData.pack(): List> { + if (BetterModel.IS_PAPER) return packAll()!! + val list = arrayListOf>() + (DATA_ITEMS[this] as Array>).forEach { + list += it.value() + } + return list } \ No newline at end of file diff --git a/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/HitBoxImpl.kt b/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/HitBoxImpl.kt index 443009c..af9cc32 100644 --- a/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/HitBoxImpl.kt +++ b/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/HitBoxImpl.kt @@ -1,5 +1,6 @@ package kr.toxicity.model.nms.v1_21_R1 +import kr.toxicity.model.api.BetterModel import kr.toxicity.model.api.data.blueprint.ModelBoundingBox import kr.toxicity.model.api.event.ModelDamagedEvent import kr.toxicity.model.api.event.ModelInteractEvent @@ -8,7 +9,8 @@ import kr.toxicity.model.api.nms.HitBox import kr.toxicity.model.api.nms.HitBoxListener import kr.toxicity.model.api.nms.TransformSupplier import net.minecraft.world.InteractionHand -import net.minecraft.world.InteractionHand.* +import net.minecraft.world.InteractionHand.MAIN_HAND +import net.minecraft.world.InteractionHand.OFF_HAND import net.minecraft.world.InteractionResult import net.minecraft.world.damagesource.DamageSource import net.minecraft.world.entity.* @@ -44,11 +46,11 @@ class HitBoxImpl( persist = false isSilent = true initialized = true - `moonrise$setUpdatingSectionStatus`(false) + if (BetterModel.IS_PAPER) `moonrise$setUpdatingSectionStatus`(false) } override fun name(): String = name - override fun source(): Entity = delegate.bukkitLivingEntity + override fun source(): Entity = delegate.bukkitEntity override fun relativePosition(): Vector3f = position().run { Vector3f(x.toFloat(), y.toFloat(), z.toFloat()) } @@ -129,14 +131,14 @@ class HitBoxImpl( MAIN_HAND -> Hand.RIGHT OFF_HAND -> Hand.LEFT }) - if (!interact.callEvent()) return InteractionResult.FAIL + if (!interact.call()) return InteractionResult.FAIL return delegate.interact(player, hand) } override fun hurt(source: DamageSource, amount: Float): Boolean { val ds = CraftDamageSource(source) val event = ModelDamagedEvent(this, ds, amount) - if (!event.callEvent()) return false + if (!event.call()) return false if (listener.damage(ds, amount.toDouble())) return false return delegate.hurt(source, event.damage) } diff --git a/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/NMSImpl.kt b/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/NMSImpl.kt index eefb8a7..83250d1 100644 --- a/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/NMSImpl.kt +++ b/nms/v1_21_R1/src/main/kotlin/kr/toxicity/model/nms/v1_21_R1/NMSImpl.kt @@ -1,5 +1,6 @@ package kr.toxicity.model.nms.v1_21_R1 +import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup import com.google.common.collect.ImmutableList import com.google.gson.JsonParser import com.mojang.datafixers.util.Pair @@ -15,6 +16,8 @@ import net.minecraft.network.protocol.Packet import net.minecraft.network.protocol.game.* import net.minecraft.network.syncher.EntityDataAccessor import net.minecraft.server.MinecraftServer +import net.minecraft.server.level.ServerLevel +import net.minecraft.server.network.ServerCommonPacketListenerImpl import net.minecraft.world.entity.Display import net.minecraft.world.entity.Display.ItemDisplay import net.minecraft.world.entity.Entity @@ -23,7 +26,9 @@ import net.minecraft.world.entity.EquipmentSlot import net.minecraft.world.entity.ai.attributes.Attributes import net.minecraft.world.item.ItemDisplayContext import net.minecraft.world.item.Items -import org.bukkit.Bukkit +import net.minecraft.world.level.entity.LevelEntityGetter +import net.minecraft.world.level.entity.LevelEntityGetterAdapter +import net.minecraft.world.level.entity.PersistentEntitySectionManager import org.bukkit.Color import org.bukkit.Location import org.bukkit.craftbukkit.CraftWorld @@ -49,6 +54,53 @@ class NMSImpl : NMS { companion object { private const val INJECT_NAME = "bettermodel_channel_handler" + //Spigot + private val getConnection: (ServerCommonPacketListenerImpl) -> Connection = if (BetterModel.IS_PAPER) { + { + it.connection + } + } else { + ServerCommonPacketListenerImpl::class.java.declaredFields.first { f -> + f.type == Connection::class.java + }.apply { + isAccessible = true + }.let { get -> + { + get[it] as Connection + } + } + } + private val entityTracker = ServerLevel::class.java.fields.firstOrNull { + it.type == PersistentEntitySectionManager::class.java + }?.apply { + isAccessible = true + } + @Suppress("UNCHECKED_CAST") + private val ServerLevel.levelGetter + get(): LevelEntityGetter { + return if (BetterModel.IS_PAPER) { + `moonrise$getEntityLookup`() + } else { + entityTracker?.get(this)?.let { + (it as PersistentEntitySectionManager<*>).entityGetter as LevelEntityGetter + } ?: throw RuntimeException("LevelEntityGetter") + } + } + private val getEntityById: (LevelEntityGetter, Int) -> Entity? = if (BetterModel.IS_PAPER) { g, i -> + (g as EntityLookup)[i] + } else LevelEntityGetterAdapter::class.java.declaredFields.first { + net.minecraft.world.level.entity.EntityLookup::class.java.isAssignableFrom(it.type) + }.let { + it.isAccessible = true + { e, i -> + (it[e] as net.minecraft.world.level.entity.EntityLookup<*>).getEntity(i) as? Entity + } + } + private fun Int.toEntity() = MinecraftServer.getServer().allLevels.firstNotNullOfOrNull { + getEntityById(it.levelGetter, this) + } + //Spigot + private fun Class<*>.serializers() = declaredFields.filter { f -> EntityDataAccessor::class.java.isAssignableFrom(f.type) } @@ -72,10 +124,6 @@ class NMSImpl : NMS { } } - private fun Int.toEntity() = MinecraftServer.getServer().allLevels.firstNotNullOfOrNull { - it.`moonrise$getEntityLookup`().get(this) - } - private fun Entity.toVoid(bundler: PacketBundlerImpl) { bundler.add(ClientboundSetEquipmentPacket(id, EquipmentSlot.entries.map { e -> Pair.of(e, Items.AIR.defaultInstance) @@ -96,7 +144,7 @@ class NMSImpl : NMS { private val entityUUIDMap = ConcurrentHashMap() init { - val pipeLine = connection.connection.channel.pipeline() + val pipeLine = getConnection(connection).channel.pipeline() pipeLine.toMap().forEach { if (it.value is Connection) pipeLine.addBefore(it.key, INJECT_NAME, this) } @@ -139,13 +187,14 @@ class NMSImpl : NMS { val e = tracker.entity val handle = (e as CraftEntity).handle entityUUIDMap.remove(handle.uuid) - send(ClientboundSetEntityDataPacket(handle.id, handle.entityData.packAll()!!)) + send(ClientboundSetEntityDataPacket(handle.id, handle.entityData.pack())) if (e is LivingEntity) { e.equipment?.let { i -> send(ClientboundSetEquipmentPacket(handle.id, org.bukkit.inventory.EquipmentSlot.entries.mapNotNull { - runCatching { - it to i.getItem(it) - }.getOrDefault(null) + val g = runCatching { + i.getItem(it) + }.getOrDefault(null) ?: return@mapNotNull null + it to g }.map { (type, item) -> Pair.of(when (type) { HAND -> EquipmentSlot.MAINHAND @@ -166,7 +215,7 @@ class NMSImpl : NMS { when (msg) { is ClientboundAddEntityPacket -> { msg.id.toEntity()?.let { e -> - Bukkit.getRegionScheduler().run(BetterModel.inst(), e.bukkitEntity.location) { + BetterModel.inst().scheduler().task(e.bukkitEntity.location) { EntityTracker.tracker(e.bukkitEntity)?.spawn(player) } } @@ -270,7 +319,7 @@ class NMSImpl : NMS { bundler.unwrap().add(addPacket) val f = display.transformationInterpolationDuration frame(0) - bundler.unwrap().add(ClientboundSetEntityDataPacket(display.id, display.entityData.packAll()!!)) + bundler.unwrap().add(ClientboundSetEntityDataPacket(display.id, display.entityData.pack())) frame(f) } @@ -330,7 +379,7 @@ class NMSImpl : NMS { private val dataPacket get(): ClientboundSetEntityDataPacket { val set = if (itemChanged) transformSetWithItem else transformSet - val result = ClientboundSetEntityDataPacket(display.id, display.entityData.packAll()!!.filter { + val result = ClientboundSetEntityDataPacket(display.id, display.entityData.pack().filter { set.contains(it.id) }) itemChanged = false @@ -400,6 +449,10 @@ class NMSImpl : NMS { return handle.attributes.getInstance(Attributes.SCALE)?.value ?: 1.0 } + override fun pitch(): Float { + return handle.xRot + } + override fun bodyYaw(): Float { return handle.visualRotationYInDegrees } diff --git a/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/Functions.kt b/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/Functions.kt index 3149253..7fae72c 100644 --- a/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/Functions.kt +++ b/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/Functions.kt @@ -1,8 +1,14 @@ package kr.toxicity.model.nms.v1_21_R2 +import kr.toxicity.model.api.BetterModel import kr.toxicity.model.api.data.blueprint.ModelBoundingBox +import net.minecraft.network.syncher.SynchedEntityData +import net.minecraft.network.syncher.SynchedEntityData.DataItem import net.minecraft.world.entity.Entity import net.minecraft.world.entity.EntityAttachment +import org.bukkit.Bukkit +import org.bukkit.event.Cancellable +import org.bukkit.event.Event import org.joml.Vector3f operator fun ModelBoundingBox.times(scale: Double) = ModelBoundingBox( @@ -18,4 +24,25 @@ fun Entity.passengerPosition(): Vector3f { return attachments.get(EntityAttachment.PASSENGER, 0, yRot).let { v -> Vector3f(v.x.toFloat(), v.y.toFloat(), v.z.toFloat()) } +} + +fun Event.call(): Boolean { + Bukkit.getPluginManager().callEvent(this) + return if (this is Cancellable) !isCancelled else true +} + +private val DATA_ITEMS = SynchedEntityData::class.java.declaredFields.first { + it.type.isArray +}.apply { + isAccessible = true +} + +@Suppress("UNCHECKED_CAST") +fun SynchedEntityData.pack(): List> { + if (BetterModel.IS_PAPER) return packAll()!! + val list = arrayListOf>() + (DATA_ITEMS[this] as Array>).forEach { + list += it.value() + } + return list } \ No newline at end of file diff --git a/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/HitBoxImpl.kt b/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/HitBoxImpl.kt index d1b50b8..74f42bd 100644 --- a/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/HitBoxImpl.kt +++ b/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/HitBoxImpl.kt @@ -1,5 +1,6 @@ package kr.toxicity.model.nms.v1_21_R2 +import kr.toxicity.model.api.BetterModel import kr.toxicity.model.api.data.blueprint.ModelBoundingBox import kr.toxicity.model.api.event.ModelDamagedEvent import kr.toxicity.model.api.event.ModelInteractEvent @@ -46,11 +47,11 @@ class HitBoxImpl( persist = false isSilent = true initialized = true - `moonrise$setUpdatingSectionStatus`(false) + if (BetterModel.IS_PAPER) `moonrise$setUpdatingSectionStatus`(false) } override fun name(): String = name - override fun source(): Entity = delegate.bukkitLivingEntity + override fun source(): Entity = delegate.bukkitEntity override fun relativePosition(): Vector3f = position().run { Vector3f(x.toFloat(), y.toFloat(), z.toFloat()) } @@ -131,7 +132,7 @@ class HitBoxImpl( MAIN_HAND -> Hand.RIGHT OFF_HAND -> Hand.LEFT }) - if (!interact.callEvent()) return InteractionResult.FAIL + if (!interact.call()) return InteractionResult.FAIL return delegate.interact(player, hand) } @@ -142,7 +143,7 @@ class HitBoxImpl( override fun hurtServer(world: ServerLevel, source: DamageSource, amount: Float): Boolean { val ds = CraftDamageSource(source) val event = ModelDamagedEvent(this, ds, amount) - if (!event.callEvent()) return false + if (!event.call()) return false if (listener.damage(ds, amount.toDouble())) return false return delegate.hurtServer(world, source, event.damage) } diff --git a/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/NMSImpl.kt b/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/NMSImpl.kt index a036d12..083614b 100644 --- a/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/NMSImpl.kt +++ b/nms/v1_21_R2/src/main/kotlin/kr/toxicity/model/nms/v1_21_R2/NMSImpl.kt @@ -1,5 +1,6 @@ package kr.toxicity.model.nms.v1_21_R2 +import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup import com.google.common.collect.ImmutableList import com.google.gson.JsonParser import com.mojang.datafixers.util.Pair @@ -15,12 +16,16 @@ import net.minecraft.network.protocol.Packet import net.minecraft.network.protocol.game.* import net.minecraft.network.syncher.EntityDataAccessor import net.minecraft.server.MinecraftServer +import net.minecraft.server.level.ServerLevel +import net.minecraft.server.network.ServerCommonPacketListenerImpl import net.minecraft.world.entity.* import net.minecraft.world.entity.Display.ItemDisplay import net.minecraft.world.entity.ai.attributes.Attributes import net.minecraft.world.item.ItemDisplayContext import net.minecraft.world.item.Items -import org.bukkit.Bukkit +import net.minecraft.world.level.entity.LevelEntityGetter +import net.minecraft.world.level.entity.LevelEntityGetterAdapter +import net.minecraft.world.level.entity.PersistentEntitySectionManager import org.bukkit.Color import org.bukkit.Location import org.bukkit.craftbukkit.CraftWorld @@ -46,6 +51,53 @@ class NMSImpl : NMS { companion object { private const val INJECT_NAME = "bettermodel_channel_handler" + //Spigot + private val getConnection: (ServerCommonPacketListenerImpl) -> Connection = if (BetterModel.IS_PAPER) { + { + it.connection + } + } else { + ServerCommonPacketListenerImpl::class.java.declaredFields.first { f -> + f.type == Connection::class.java + }.apply { + isAccessible = true + }.let { get -> + { + get[it] as Connection + } + } + } + private val entityTracker = ServerLevel::class.java.fields.firstOrNull { + it.type == PersistentEntitySectionManager::class.java + }?.apply { + isAccessible = true + } + @Suppress("UNCHECKED_CAST") + private val ServerLevel.levelGetter + get(): LevelEntityGetter { + return if (BetterModel.IS_PAPER) { + `moonrise$getEntityLookup`() + } else { + entityTracker?.get(this)?.let { + (it as PersistentEntitySectionManager<*>).entityGetter as LevelEntityGetter + } ?: throw RuntimeException("LevelEntityGetter") + } + } + private val getEntityById: (LevelEntityGetter, Int) -> Entity? = if (BetterModel.IS_PAPER) { g, i -> + (g as EntityLookup)[i] + } else LevelEntityGetterAdapter::class.java.declaredFields.first { + net.minecraft.world.level.entity.EntityLookup::class.java.isAssignableFrom(it.type) + }.let { + it.isAccessible = true + { e, i -> + (it[e] as net.minecraft.world.level.entity.EntityLookup<*>).getEntity(i) as? Entity + } + } + private fun Int.toEntity() = MinecraftServer.getServer().allLevels.firstNotNullOfOrNull { + getEntityById(it.levelGetter, this) + } + //Spigot + private fun Class<*>.serializers() = declaredFields.filter { f -> EntityDataAccessor::class.java.isAssignableFrom(f.type) } @@ -69,10 +121,6 @@ class NMSImpl : NMS { } } - private fun Int.toEntity() = MinecraftServer.getServer().allLevels.firstNotNullOfOrNull { - it.`moonrise$getEntityLookup`().get(this) - } - private fun Entity.toVoid(bundler: PacketBundlerImpl) { bundler.add(ClientboundSetEquipmentPacket(id, EquipmentSlot.entries.map { e -> Pair.of(e, Items.AIR.defaultInstance) @@ -93,7 +141,7 @@ class NMSImpl : NMS { private val entityUUIDMap = ConcurrentHashMap() init { - val pipeLine = connection.connection.channel.pipeline() + val pipeLine = getConnection(connection).channel.pipeline() pipeLine.toMap().forEach { if (it.value is Connection) pipeLine.addBefore(it.key, INJECT_NAME, this) } @@ -137,13 +185,14 @@ class NMSImpl : NMS { val e = tracker.entity val handle = (e as CraftEntity).handle entityUUIDMap.remove(handle.uuid) - send(ClientboundSetEntityDataPacket(handle.id, handle.entityData.packAll()!!)) + send(ClientboundSetEntityDataPacket(handle.id, handle.entityData.pack())) if (e is LivingEntity) { e.equipment?.let { i -> send(ClientboundSetEquipmentPacket(handle.id, org.bukkit.inventory.EquipmentSlot.entries.mapNotNull { - runCatching { - it to i.getItem(it) - }.getOrDefault(null) + val g = runCatching { + i.getItem(it) + }.getOrDefault(null) ?: return@mapNotNull null + it to g }.map { (type, item) -> Pair.of(when (type) { HAND -> EquipmentSlot.MAINHAND @@ -164,7 +213,7 @@ class NMSImpl : NMS { when (msg) { is ClientboundAddEntityPacket -> { msg.id.toEntity()?.let { e -> - Bukkit.getRegionScheduler().run(BetterModel.inst(), e.bukkitEntity.location) { + BetterModel.inst().scheduler().task(e.bukkitEntity.location) { EntityTracker.tracker(e.bukkitEntity)?.spawn(player) } } @@ -269,7 +318,7 @@ class NMSImpl : NMS { bundler.unwrap().add(addPacket) val f = display.transformationInterpolationDuration frame(0) - bundler.unwrap().add(ClientboundSetEntityDataPacket(display.id, display.entityData.packAll()!!)) + bundler.unwrap().add(ClientboundSetEntityDataPacket(display.id, display.entityData.pack())) frame(f) } @@ -329,7 +378,7 @@ class NMSImpl : NMS { private val dataPacket get(): ClientboundSetEntityDataPacket { val set = if (itemChanged) transformSetWithItem else transformSet - val result = ClientboundSetEntityDataPacket(display.id, display.entityData.packAll()!!.filter { + val result = ClientboundSetEntityDataPacket(display.id, display.entityData.pack().filter { set.contains(it.id) }) itemChanged = false @@ -402,6 +451,10 @@ class NMSImpl : NMS { return handle.attributes.getInstance(Attributes.SCALE)?.value ?: 1.0 } + override fun pitch(): Float { + return handle.xRot + } + override fun bodyYaw(): Float { return handle.visualRotationYInDegrees } diff --git a/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/Functions.kt b/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/Functions.kt index a933253..882997a 100644 --- a/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/Functions.kt +++ b/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/Functions.kt @@ -1,9 +1,14 @@ package kr.toxicity.model.nms.v1_21_R3 +import kr.toxicity.model.api.BetterModel import kr.toxicity.model.api.data.blueprint.ModelBoundingBox +import net.minecraft.network.syncher.SynchedEntityData +import net.minecraft.network.syncher.SynchedEntityData.DataItem import net.minecraft.world.entity.Entity import net.minecraft.world.entity.EntityAttachment -import net.minecraft.world.phys.AABB +import org.bukkit.Bukkit +import org.bukkit.event.Cancellable +import org.bukkit.event.Event import org.joml.Vector3f operator fun ModelBoundingBox.times(scale: Double) = ModelBoundingBox( @@ -19,4 +24,25 @@ fun Entity.passengerPosition(): Vector3f { return attachments.get(EntityAttachment.PASSENGER, 0, yRot).let { v -> Vector3f(v.x.toFloat(), v.y.toFloat(), v.z.toFloat()) } +} + +fun Event.call(): Boolean { + Bukkit.getPluginManager().callEvent(this) + return if (this is Cancellable) !isCancelled else true +} + +private val DATA_ITEMS = SynchedEntityData::class.java.declaredFields.first { + it.type.isArray +}.apply { + isAccessible = true +} + +@Suppress("UNCHECKED_CAST") +fun SynchedEntityData.pack(): List> { + if (BetterModel.IS_PAPER) return packAll()!! + val list = arrayListOf>() + (DATA_ITEMS[this] as Array>).forEach { + list += it.value() + } + return list } \ No newline at end of file diff --git a/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/HitBoxImpl.kt b/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/HitBoxImpl.kt index b3a50bb..cd1b9f0 100644 --- a/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/HitBoxImpl.kt +++ b/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/HitBoxImpl.kt @@ -1,5 +1,6 @@ package kr.toxicity.model.nms.v1_21_R3 +import kr.toxicity.model.api.BetterModel import kr.toxicity.model.api.data.blueprint.ModelBoundingBox import kr.toxicity.model.api.event.ModelDamagedEvent import kr.toxicity.model.api.event.ModelInteractEvent @@ -46,11 +47,11 @@ class HitBoxImpl( persist = false isSilent = true initialized = true - `moonrise$setUpdatingSectionStatus`(false) + if (BetterModel.IS_PAPER) `moonrise$setUpdatingSectionStatus`(false) } override fun name(): String = name - override fun source(): Entity = delegate.bukkitLivingEntity + override fun source(): Entity = delegate.bukkitEntity override fun relativePosition(): Vector3f = position().run { Vector3f(x.toFloat(), y.toFloat(), z.toFloat()) } @@ -131,7 +132,7 @@ class HitBoxImpl( MAIN_HAND -> Hand.RIGHT OFF_HAND -> Hand.LEFT }) - if (!interact.callEvent()) return InteractionResult.FAIL + if (!interact.call()) return InteractionResult.FAIL return delegate.interact(player, hand) } @@ -142,7 +143,7 @@ class HitBoxImpl( override fun hurtServer(world: ServerLevel, source: DamageSource, amount: Float): Boolean { val ds = CraftDamageSource(source) val event = ModelDamagedEvent(this, ds, amount) - if (!event.callEvent()) return false + if (!event.call()) return false if (listener.damage(ds, amount.toDouble())) return false return delegate.hurtServer(world, source, event.damage) } diff --git a/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/NMSImpl.kt b/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/NMSImpl.kt index 1495fce..adebd1f 100644 --- a/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/NMSImpl.kt +++ b/nms/v1_21_R3/src/main/kotlin/kr/toxicity/model/nms/v1_21_R3/NMSImpl.kt @@ -1,5 +1,6 @@ package kr.toxicity.model.nms.v1_21_R3 +import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup import com.google.common.collect.ImmutableList import com.google.gson.JsonParser import com.mojang.datafixers.util.Pair @@ -16,6 +17,8 @@ import net.minecraft.network.protocol.Packet import net.minecraft.network.protocol.game.* import net.minecraft.network.syncher.EntityDataAccessor import net.minecraft.server.MinecraftServer +import net.minecraft.server.level.ServerLevel +import net.minecraft.server.network.ServerCommonPacketListenerImpl import net.minecraft.world.entity.* import net.minecraft.world.entity.Display.ItemDisplay import net.minecraft.world.entity.ai.attributes.Attributes @@ -23,7 +26,9 @@ import net.minecraft.world.item.ItemDisplayContext import net.minecraft.world.item.Items import net.minecraft.world.item.component.CustomModelData import net.minecraft.world.item.component.DyedItemColor -import org.bukkit.Bukkit +import net.minecraft.world.level.entity.LevelEntityGetter +import net.minecraft.world.level.entity.LevelEntityGetterAdapter +import net.minecraft.world.level.entity.PersistentEntitySectionManager import org.bukkit.Location import org.bukkit.craftbukkit.CraftWorld import org.bukkit.craftbukkit.entity.CraftEntity @@ -47,6 +52,53 @@ class NMSImpl : NMS { companion object { private const val INJECT_NAME = "bettermodel_channel_handler" + //Spigot + private val getConnection: (ServerCommonPacketListenerImpl) -> Connection = if (BetterModel.IS_PAPER) { + { + it.connection + } + } else { + ServerCommonPacketListenerImpl::class.java.declaredFields.first { f -> + f.type == Connection::class.java + }.apply { + isAccessible = true + }.let { get -> + { + get[it] as Connection + } + } + } + private val entityTracker = ServerLevel::class.java.fields.firstOrNull { + it.type == PersistentEntitySectionManager::class.java + }?.apply { + isAccessible = true + } + @Suppress("UNCHECKED_CAST") + private val ServerLevel.levelGetter + get(): LevelEntityGetter { + return if (BetterModel.IS_PAPER) { + `moonrise$getEntityLookup`() + } else { + entityTracker?.get(this)?.let { + (it as PersistentEntitySectionManager<*>).entityGetter as LevelEntityGetter + } ?: throw RuntimeException("LevelEntityGetter") + } + } + private val getEntityById: (LevelEntityGetter, Int) -> Entity? = if (BetterModel.IS_PAPER) { g, i -> + (g as EntityLookup)[i] + } else LevelEntityGetterAdapter::class.java.declaredFields.first { + net.minecraft.world.level.entity.EntityLookup::class.java.isAssignableFrom(it.type) + }.let { + it.isAccessible = true + { e, i -> + (it[e] as net.minecraft.world.level.entity.EntityLookup<*>).getEntity(i) as? Entity + } + } + private fun Int.toEntity() = MinecraftServer.getServer().allLevels.firstNotNullOfOrNull { + getEntityById(it.levelGetter, this) + } + //Spigot + private fun Class<*>.serializers() = declaredFields.filter { f -> EntityDataAccessor::class.java.isAssignableFrom(f.type) } @@ -70,10 +122,6 @@ class NMSImpl : NMS { } } - private fun Int.toEntity() = MinecraftServer.getServer().allLevels.firstNotNullOfOrNull { - it.`moonrise$getEntityLookup`().get(this) - } - private fun Entity.toVoid(bundler: PacketBundlerImpl) { bundler.add(ClientboundSetEquipmentPacket(id, EquipmentSlot.entries.map { e -> Pair.of(e, Items.AIR.defaultInstance) @@ -95,7 +143,7 @@ class NMSImpl : NMS { init { - val pipeLine = connection.connection.channel.pipeline() + val pipeLine = getConnection(connection).channel.pipeline() pipeLine.toMap().forEach { if (it.value is Connection) pipeLine.addBefore(it.key, INJECT_NAME, this) } @@ -139,13 +187,14 @@ class NMSImpl : NMS { val e = tracker.entity val handle = (e as CraftEntity).handle entityUUIDMap.remove(handle.uuid) - send(ClientboundSetEntityDataPacket(handle.id, handle.entityData.packAll()!!)) + send(ClientboundSetEntityDataPacket(handle.id, handle.entityData.pack())) if (e is LivingEntity) { e.equipment?.let { i -> send(ClientboundSetEquipmentPacket(handle.id, org.bukkit.inventory.EquipmentSlot.entries.mapNotNull { - runCatching { - it to i.getItem(it) - }.getOrDefault(null) + val g = runCatching { + i.getItem(it) + }.getOrDefault(null) ?: return@mapNotNull null + it to g }.map { (type, item) -> Pair.of(when (type) { HAND -> EquipmentSlot.MAINHAND @@ -166,7 +215,7 @@ class NMSImpl : NMS { when (msg) { is ClientboundAddEntityPacket -> { msg.id.toEntity()?.let { e -> - Bukkit.getRegionScheduler().run(BetterModel.inst(), e.bukkitEntity.location) { + BetterModel.inst().scheduler().task(e.bukkitEntity.location) { EntityTracker.tracker(e.bukkitEntity)?.spawn(player) } } @@ -270,7 +319,7 @@ class NMSImpl : NMS { bundler.unwrap().add(display.addPacket) val f = display.transformationInterpolationDuration frame(0) - bundler.unwrap().add(ClientboundSetEntityDataPacket(display.id, display.entityData.packAll()!!)) + bundler.unwrap().add(ClientboundSetEntityDataPacket(display.id, display.entityData.pack())) frame(f) } @@ -330,7 +379,7 @@ class NMSImpl : NMS { private val dataPacket get(): ClientboundSetEntityDataPacket { val set = if (itemChanged) transformSetWithItem else transformSet - val result = ClientboundSetEntityDataPacket(display.id, display.entityData.packAll()!!.filter { + val result = ClientboundSetEntityDataPacket(display.id, display.entityData.pack().filter { set.contains(it.id) }) itemChanged = false @@ -399,6 +448,10 @@ class NMSImpl : NMS { return handle.attributes.getInstance(Attributes.SCALE)?.value ?: 1.0 } + override fun pitch(): Float { + return handle.xRot + } + override fun bodyYaw(): Float { return handle.visualRotationYInDegrees }