Compare commits

...
Author SHA1 Message Date
Owen1212055 daa4fe5fb5 Use hasImpulse vs hurtMarked for setVelocity
This will prevent projectiles from using the less accurate path for updating motion when interfaced with the API. However, hasImpulse is the more proper way of marking that the entity needs its velocity updated anyways.

This seems to be fairly unused logic in vanilla
2025-06-23 22:39:41 -04:00
Owen1212055 068a1ccda1 Improve AbstractHurtingProjectile syncing
I am sure this has been reported before but I cannot search for any bugs due to Mojang's new bug tracker.

Projectiles at high velocities very obviously go off track from their original course when sent to the client. This is due to the aggressive downsizing of velocity when sent over the client, and these entities typically have large velocity and still work just fine. We workaround this by sending the velocity for these entities exactly by using a teleport packet with relative flags and zero positional data. This prevents any possible position syncing, but sends an accurate velocity.

Then, we properly send the ClientboundProjectilePowerPacket on the first tick the entity is created, as vanilla only updates this when the delta is updated typically.
2025-06-23 22:36:49 -04:00
Owen1212055 10c7914592 Remove move this.update() usages
These can be removed as these values can now be updated on the client through synched values, additionally remove the extra bit that triggered redstone on rotation update which is not vanilla behavior.
2025-06-23 22:28:24 -04:00
Owen1212055 c2e1beaec8 Fix MC-297817
This issue causes the item frame to move on the client erroneously, so we force the entity to resend its position the next tick.

This utilizes a moonrise patch which has a resync flag that can be triggered for the next tick, so we just utilize this. I stub out the method to ensure that it still compiles without the feature patch.
2025-06-23 22:27:17 -04:00
11 changed files with 101 additions and 81 deletions

No files matched your search

@@ -46,10 +46,6 @@ public interface Fireball extends Projectile, Explosive {
* The acceleration gets applied to the velocity every tick, depending on
* the specific type of the fireball a damping / drag factor is applied so
* that the velocity does not grow into infinity.
* <br>
* <b>Note:</b> that the client may not respect non-default acceleration
* power and will therefore mispredict the location of the fireball, causing
* visual stutter.
*
* @param acceleration the acceleration
*/
@@ -354,7 +354,7 @@ index 0000000000000000000000000000000000000000..ae2bb9a73106febfe5f0d090abd4252b
+ }
+}
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 7b5ed00c9b2f22af5cbc44171192d674936dc7d7..5fe3c9a159908785e08fa874982bc1a82283bb1d 100644
index d51645b115780dac9ff6010806e8bd62dedc8e9f..3faf1b5556c55f3468182f75b2dbff4aaa3aae3a 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableList;
@@ -484,7 +484,7 @@ index c70a58f5f633fa8e255f74c42f5e87c96b7b013a..ec20a5a6d7c8f65abda528fec36bec7b
public void tick() {
super.tick();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index f961540a00bfb5e1c8eb0e739f8ae535e9eee8f3..7453ddb09f349b7836f966573e4933646a75cba6 100644
index 7c7b9872a2cb16d842901a86e7e4b0f723588ae0..c8ce2ccfd5e5e8bdc7dc10ed7b72ca8d43444b4e 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -409,6 +409,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -517,7 +517,7 @@ index f961540a00bfb5e1c8eb0e739f8ae535e9eee8f3..7453ddb09f349b7836f966573e493364
SynchedEntityData.Builder builder = new SynchedEntityData.Builder(this);
builder.define(DATA_SHARED_FLAGS_ID, (byte)0);
builder.define(DATA_AIR_SUPPLY_ID, this.getMaxAirSupply());
@@ -984,6 +1000,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -995,6 +1011,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
} else {
if (type == MoverType.PISTON) {
@@ -528,7 +528,7 @@ index f961540a00bfb5e1c8eb0e739f8ae535e9eee8f3..7453ddb09f349b7836f966573e493364
movement = this.limitPistonMovement(movement);
if (movement.equals(Vec3.ZERO)) {
return;
@@ -997,6 +1017,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -1008,6 +1028,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
this.stuckSpeedMultiplier = Vec3.ZERO;
this.setDeltaMovement(Vec3.ZERO);
}
@@ -695,7 +695,7 @@ index 548d7c8dc517da6c4db86b11f579ae63e6db56cf..a29860af4c37b2b45df49f9ba18f7e38
public void tick() {
if (this.getItem().isEmpty()) {
diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java
index b0607f4a9b35570b319423c7876bb904d5154e8e..98c8653647dc52059d8becfe38a74d4e62edf08f 100644
index ef8347329b440833b45a54be2b6e4204ac0a425e..43f16df230f87a43e249a58fc10ef2da517f22ee 100644
--- a/net/minecraft/world/entity/npc/Villager.java
+++ b/net/minecraft/world/entity/npc/Villager.java
@@ -269,11 +269,35 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
@@ -838,7 +838,7 @@ index 52acc72841f0c6980f5f3f8ef21d0b29dd472ce3..41a6ec508a10a49a37539d2f10171d15
+
}
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index d26e4d85d8fd8bd4f0c7de30b50a2ce370b37bf5..bab28e7afb7b1249d40631aabff16fc18cf95ea0 100644
index 06069d3ac598f5f12feab038de4f1199794298f6..980eaba27ce2616c1573a4760cf4acc2dd251190 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -143,6 +143,12 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
@@ -26910,11 +26910,11 @@ index 92a39927d7bd9af3ab67c2595d8ed435a2da6a69..7c9a2eed4441f816723562e0012f918d
}
}
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 283f240511898e416b83ec4dcccd4901889b80bb..3d5a8163a7dd78a195b77c4aaebdee2dc3dee64b 100644
index 5f8ce58e8af0590f0f79ff7122ad8a5ac96a5d9b..50bde815b917c14bc4fb9163bd9df45bfa63ef47 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -104,6 +104,11 @@ public class ServerEntity {
}
@@ -122,6 +122,11 @@ public class ServerEntity {
// Paper end - Improve AbstractHurtingProjectile syncing
public void sendChanges() {
+ // Paper start - optimise collisions
@@ -28723,7 +28723,7 @@ index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b896
+ // Paper end - block counting
}
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef1e3bfbcb 100644
index f185440ef2d2340f1b7e8db85ea2287478edf6b8..c6bc9b1bf6780c0fa4819e7b773b487416c18ea6 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -147,7 +147,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter;
@@ -28974,7 +28974,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
public Entity(EntityType<?> entityType, Level level) {
this.type = entityType;
@@ -1378,35 +1484,77 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -1389,35 +1495,77 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
return distance;
}
@@ -29076,7 +29076,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
}
private static float[] collectCandidateStepUpHeights(AABB box, List<VoxelShape> colliders, float deltaY, float maxUpStep) {
@@ -2680,21 +2828,110 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -2691,21 +2839,110 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
public boolean isInWall() {
@@ -29198,7 +29198,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
}
public InteractionResult interact(Player player, InteractionHand hand) {
@@ -4291,15 +4528,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -4302,15 +4539,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
public Iterable<Entity> getIndirectPassengers() {
@@ -29224,7 +29224,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
}
public int countPlayerPassengers() {
@@ -4442,77 +4681,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -4453,77 +4692,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
return Mth.lerp(partialTick, this.yRotO, this.yRot);
}
@@ -29415,7 +29415,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
public boolean touchingUnloadedChunk() {
AABB aabb = this.getBoundingBox().inflate(1.0);
@@ -4667,6 +4965,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -4678,6 +4976,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) {
@@ -29431,7 +29431,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
if (!checkPosition(this, x, y, z)) {
return;
}
@@ -4828,6 +5135,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -4839,6 +5146,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@Override
public final void setRemoved(Entity.RemovalReason removalReason, @Nullable org.bukkit.event.entity.EntityRemoveEvent.Cause cause) { // CraftBukkit - add Bukkit remove cause
@@ -29444,7 +29444,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit
final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers
if (this.removalReason == null) {
@@ -4838,7 +5151,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -4849,7 +5162,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
this.stopRiding();
}
@@ -29453,7 +29453,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
this.levelCallback.onRemove(removalReason);
this.onRemoval(removalReason);
// Paper start - Folia schedulers
@@ -4872,7 +5185,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -4883,7 +5196,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
public boolean shouldBeSaved() {
return (this.removalReason == null || this.removalReason.shouldSave())
&& !this.isPassenger()
@@ -60,24 +60,21 @@ index 61692c07dfb75ca0c19f603aafc96c0817861107..56a1d081a28e8b38384cfca732b10346
} else if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 3d5a8163a7dd78a195b77c4aaebdee2dc3dee64b..e96d4dee14c05f2fa329bfb1588ec795d4e3d730 100644
index 50bde815b917c14bc4fb9163bd9df45bfa63ef47..dec11820b80094656e89e7384c4b515948280121 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -103,6 +103,13 @@ public class ServerEntity {
this.trackedDataValues = entity.getEntityData().getNonDefaultValues();
@@ -104,8 +104,9 @@ public class ServerEntity {
}
+ // Paper start - fix desync when a player is added to the tracker
// Paper start - fix desync when a player is added to the tracker
+ private boolean forceStateResync;
+ public void onPlayerAdd() {
public void onPlayerAdd() {
- // TODO - IMPLEMENT STUBBED METHOD FROM MOONRISE
+ this.forceStateResync = true;
+ }
+ // Paper end - fix desync when a player is added to the tracker
+
public void sendChanges() {
// Paper start - optimise collisions
if (((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)this.entity).moonrise$isHardColliding()) {
@@ -141,7 +148,7 @@ public class ServerEntity {
}
// Paper end - fix desync when a player is added to the tracker
// Paper start - Improve AbstractHurtingProjectile syncing
@@ -159,7 +160,7 @@ public class ServerEntity {
this.sendDirtyEntityData();
}
@@ -86,7 +83,7 @@ index 3d5a8163a7dd78a195b77c4aaebdee2dc3dee64b..e96d4dee14c05f2fa329bfb1588ec795
byte b = Mth.packDegrees(this.entity.getYRot());
byte b1 = Mth.packDegrees(this.entity.getXRot());
boolean flag = Math.abs(b - this.lastSentYRot) >= 1 || Math.abs(b1 - this.lastSentXRot) >= 1;
@@ -176,7 +183,7 @@ public class ServerEntity {
@@ -194,7 +195,7 @@ public class ServerEntity {
long l1 = this.positionCodec.encodeY(vec3);
long l2 = this.positionCodec.encodeZ(vec3);
boolean flag5 = l < -32768L || l > 32767L || l1 < -32768L || l1 > 32767L || l2 < -32768L || l2 > 32767L;
@@ -95,7 +92,7 @@ index 3d5a8163a7dd78a195b77c4aaebdee2dc3dee64b..e96d4dee14c05f2fa329bfb1588ec795
|| flag5
|| this.teleportDelay > 400
|| this.wasRiding
@@ -245,6 +252,7 @@ public class ServerEntity {
@@ -263,6 +264,7 @@ public class ServerEntity {
}
this.entity.hasImpulse = false;
@@ -28,7 +28,30 @@
this.level = level;
this.broadcast = broadcast;
this.entity = entity;
@@ -103,16 +_,22 @@
@@ -98,21 +_,45 @@
this.trackedDataValues = entity.getEntityData().getNonDefaultValues();
}
+ // Paper start - fix desync when a player is added to the tracker
+ public void onPlayerAdd() {
+ // TODO - IMPLEMENT STUBBED METHOD FROM MOONRISE
+ }
+ // Paper end - fix desync when a player is added to the tracker
+ // Paper start - Improve AbstractHurtingProjectile syncing
+ // This can be removed when velocity is no longer downsized when sending it over the protocol.
+ // We create a packet that only updates the velocity on the client.
+ private static final Set<net.minecraft.world.entity.Relative> RELATIVES = net.minecraft.world.entity.Relative.union(Set.of(net.minecraft.world.entity.Relative.X, net.minecraft.world.entity.Relative.Y, net.minecraft.world.entity.Relative.Z), net.minecraft.world.entity.Relative.ROTATION);
+ public Packet<ClientGamePacketListener> getAccurateVelocityPacket() {
+ return new net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket(this.entity.getId(),
+ new net.minecraft.world.entity.PositionMoveRotation(net.minecraft.world.phys.Vec3.ZERO, this.lastSentMovement, 0, 0),
+ RELATIVES,
+ this.wasOnGround
+ );
+ }
+ // Paper end - Improve AbstractHurtingProjectile syncing
+
public void sendChanges() {
List<Entity> passengers = this.entity.getPassengers();
if (!passengers.equals(this.lastPassengers)) {
List<UUID> list = this.mountedOrDismounted(passengers).map(Entity::getUUID).toList();
this.broadcastWithIgnore.accept(new ClientboundSetPassengersPacket(this.entity), list);
@@ -70,6 +93,15 @@
Packet<?> packet = null;
boolean flag2 = flag1 || this.tickCount % 60 == 0;
boolean flag3 = false;
@@ -188,7 +_,7 @@
.accept(
new ClientboundBundlePacket(
List.of(
- new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement),
+ this.getAccurateVelocityPacket(), // Paper - Improve AbstractHurtingProjectile syncing
new ClientboundProjectilePowerPacket(abstractHurtingProjectile.getId(), abstractHurtingProjectile.accelerationPower)
)
)
@@ -227,6 +_,25 @@
this.tickCount++;
@@ -131,6 +163,20 @@
}
if (!this.entity.getPassengers().isEmpty()) {
@@ -326,6 +_,13 @@
if (this.entity instanceof Leashable leashable && leashable.isLeashed()) {
consumer.accept(new ClientboundSetEntityLinkPacket(this.entity, leashable.getLeashHolder()));
}
+ // Paper start - Improve AbstractHurtingProjectile syncing
+ // We need to send our more accurate velocity and our acceleration power
+ if (this.entity instanceof AbstractHurtingProjectile abstractHurtingProjectile) {
+ consumer.accept(new ClientboundProjectilePowerPacket(abstractHurtingProjectile.getId(), abstractHurtingProjectile.accelerationPower));
+ consumer.accept(this.getAccurateVelocityPacket());
+ }
+ // Paper end - Improve AbstractHurtingProjectile syncing
}
public Vec3 getPositionBase() {
@@ -359,6 +_,11 @@
if (this.entity instanceof LivingEntity) {
Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync();
@@ -227,7 +227,7 @@
}
protected abstract void defineSynchedData(SynchedEntityData.Builder builder);
@@ -384,6 +_,48 @@
@@ -384,6 +_,59 @@
return this.entityData;
}
@@ -271,6 +271,17 @@
+
+ to.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket(this.id, values));
+ }
+
+ // This method forces the entity position to be resynced the next tick, using the exact position.
+ public void resendPossiblyDesyncedPosition() {
+ ServerLevel world = (net.minecraft.server.level.ServerLevel)this.level();
+ net.minecraft.server.level.ChunkMap.TrackedEntity tracker = world == null ? null : world.getChunkSource().chunkMap.entityMap.get(this.getId());
+ if (tracker == null) {
+ return;
+ }
+ final net.minecraft.server.level.ServerEntity serverEntity = tracker.serverEntity;
+ serverEntity.onPlayerAdd();
+ }
+ // Paper end
+
@Override
@@ -8,6 +8,14 @@
public ItemFrame(EntityType<? extends ItemFrame> entityType, Level level) {
super(entityType, level);
@@ -92,6 +_,7 @@
this.xRotO = this.getXRot();
this.yRotO = this.getYRot();
this.recalculateBoundingBox();
+ this.resendPossiblyDesyncedPosition(); // Paper - Fix MC-297817
}
@Override
@@ -102,6 +_,12 @@
@Override
@@ -200,7 +200,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
}
// Paper end
this.entity.setDeltaMovement(CraftVector.toVec3(velocity));
this.entity.hurtMarked = true;
this.entity.hasImpulse = true;
}
/**
@@ -62,9 +62,10 @@ public class CraftFireball extends AbstractProjectile implements Fireball {
public void setAcceleration(@NotNull Vector acceleration) {
Preconditions.checkArgument(acceleration != null, "Vector acceleration cannot be null");
// SPIGOT-6993: AbstractHurtingProjectile#assignDirectionalMovement will normalize the given values
// Note: Because of MC-80142 the fireball will stutter on the client when setting the power to something other than 0 or the normalized vector * 0.1
this.getHandle().assignDirectionalMovement(CraftVector.toVec3(acceleration), acceleration.length());
this.update(); // SPIGOT-6579
// Set the acceleration power in order to properly sync the movement to the client
double length = acceleration.length();
this.getHandle().accelerationPower = length;
this.getHandle().assignDirectionalMovement(CraftVector.toVec3(acceleration), length);
}
@NotNull
@@ -22,35 +22,6 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame {
return (net.minecraft.world.entity.decoration.ItemFrame) this.entity;
}
@Override
public boolean setFacingDirection(BlockFace face, boolean force) {
HangingEntity hanging = this.getHandle();
Direction oldDir = hanging.getDirection();
Direction newDir = CraftBlock.blockFaceToNotch(face);
Preconditions.checkArgument(newDir != null, "%s is not a valid facing direction", face);
this.getHandle().setDirection(newDir);
if (!force && !this.getHandle().generation && !hanging.survives()) {
hanging.setDirection(oldDir);
return false;
}
this.update();
return true;
}
@Override
protected void update() {
super.update();
// update redstone
if (!this.getHandle().generation) {
this.getHandle().level().updateNeighbourForOutputSignal(this.getHandle().getPos(), Blocks.AIR);
}
}
@Override
public void setItem(org.bukkit.inventory.ItemStack item) {
this.setItem(item, true);
@@ -44,14 +44,4 @@ public class CraftPainting extends CraftHanging implements Painting {
//this.update(); Paper - Don't resent entity on art update
return true;
}
@Override
public boolean setFacingDirection(BlockFace face, boolean force) {
if (super.setFacingDirection(face, force)) {
this.update();
return true;
}
return false;
}
}