Compare commits

...
Author SHA1 Message Date
Florian Reuth 9f13c68989 Fix multiple boss bars in 1.8->1.9
1.8 clients only display a single boss bar, so only one tracked bar is attached to the connection at a time. This roughly emulates how the 1.8 client processes the render order (without handling things like the FOV)
2026-11-05 21:24:33 +01:00
4 changed files with 143 additions and 14 deletions

No files matched your search

@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableList;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.EulerAngle;
import com.viaversion.viaversion.api.minecraft.Vector;
import com.viaversion.viaversion.api.minecraft.Vector3d;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_9;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
@@ -112,6 +113,15 @@ public class EntityPacketRewriter1_9 extends EntityRewriter<ClientboundPackets1_
map(Types.BOOLEAN); // 6 - On Ground
handler(wrapper -> {
final int entityId = wrapper.get(Types.VAR_INT, 0);
final double x = wrapper.get(Types.DOUBLE, 0);
final double y = wrapper.get(Types.DOUBLE, 1);
final double z = wrapper.get(Types.DOUBLE, 2);
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
tracker.setBossPosition(entityId, new Vector3d(x, y, z));
});
handler(wrapper -> {
int entityID = wrapper.get(Types.VAR_INT, 0);
if (Via.getConfig().isHologramPatch()) {
@@ -140,6 +150,7 @@ public class EntityPacketRewriter1_9 extends EntityRewriter<ClientboundPackets1_
map(Types.BYTE); // 5 - Pitch
map(Types.BOOLEAN); // 6 - On Ground
handler(wrapper -> moveBoss(wrapper, protocol));
}
});
protocol.registerClientbound(ClientboundPackets1_8.MOVE_ENTITY_POS, new PacketHandlers() {
@@ -152,6 +163,7 @@ public class EntityPacketRewriter1_9 extends EntityRewriter<ClientboundPackets1_
map(Types.BYTE, toNewShort); // 3 - Z
map(Types.BOOLEAN); // 4 - On Ground
handler(wrapper -> moveBoss(wrapper, protocol));
}
});
protocol.registerClientbound(ClientboundPackets1_8.SET_EQUIPPED_ITEM, new PacketHandlers() {
@@ -379,6 +391,17 @@ public class EntityPacketRewriter1_9 extends EntityRewriter<ClientboundPackets1_
});
}
private static void moveBoss(PacketWrapper wrapper, Protocol1_8To1_9 protocol) {
// Deltas were already converted to 1.9 units (1/4096 of a block)
final int entityId = wrapper.get(Types.VAR_INT, 0);
final double deltaX = wrapper.get(Types.SHORT, 0) / 4096D;
final double deltaY = wrapper.get(Types.SHORT, 1) / 4096D;
final double deltaZ = wrapper.get(Types.SHORT, 2) / 4096D;
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
tracker.moveBoss(entityId, deltaX, deltaY, deltaZ);
}
@Override
protected void registerRewrites() {
filter().handler(this::handleEntityData);
@@ -25,6 +25,7 @@ import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.GameMode;
import com.viaversion.viaversion.api.minecraft.Vector3d;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_9;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
@@ -111,6 +112,17 @@ public class PlayerPacketRewriter1_9 {
map(Types.BYTE); // 5 - Player Flags
handler(wrapper -> {
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
final Vector3d position = tracker.getPlayerPosition();
final byte flags = wrapper.get(Types.BYTE, 0);
final double x = wrapper.get(Types.DOUBLE, 0) + ((flags & 0x01) != 0 ? position.x() : 0);
final double y = wrapper.get(Types.DOUBLE, 1) + ((flags & 0x02) != 0 ? position.y() : 0);
final double z = wrapper.get(Types.DOUBLE, 2) + ((flags & 0x04) != 0 ? position.z() : 0);
tracker.setPlayerPosition(new Vector3d(x, y, z));
});
create(Types.VAR_INT, 0); // 6 - Teleport ID was added
}
});
@@ -449,6 +461,10 @@ public class PlayerPacketRewriter1_9 {
tracker.incrementIdlePacket();
tracker.setGround(wrapper.get(Types.BOOLEAN, 0));
};
final PacketHandler positionHandler = wrapper -> {
EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
tracker.setPlayerPosition(new Vector3d(wrapper.get(Types.DOUBLE, 0), wrapper.get(Types.DOUBLE, 1), wrapper.get(Types.DOUBLE, 2)));
};
protocol.registerServerbound(ServerboundPackets1_9.MOVE_PLAYER_POS, new PacketHandlers() {
@Override
public void register() {
@@ -457,6 +473,7 @@ public class PlayerPacketRewriter1_9 {
map(Types.DOUBLE); // 2 - Z
map(Types.BOOLEAN); // 3 - Ground
handler(onGroundHandler);
handler(positionHandler);
}
});
protocol.registerServerbound(ServerboundPackets1_9.MOVE_PLAYER_POS_ROT, new PacketHandlers() {
@@ -469,6 +486,7 @@ public class PlayerPacketRewriter1_9 {
map(Types.FLOAT); // 4 - Pitch
map(Types.BOOLEAN); // 5 - Ground
handler(onGroundHandler);
handler(positionHandler);
}
});
protocol.registerServerbound(ServerboundPackets1_9.MOVE_PLAYER_ROT, new PacketHandlers() {
@@ -18,6 +18,7 @@
package com.viaversion.viaversion.protocols.v1_8to1_9.rewriter;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.minecraft.Vector3d;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_9;
@@ -198,6 +199,17 @@ public class SpawnPacketRewriter1_9 {
map(Types.SHORT); // 10 - Velocity Y
map(Types.SHORT); // 11 - Velocity Z
// Has to happen before the entity data is handled, which may create the boss bar
handler(wrapper -> {
final int entityId = wrapper.get(Types.VAR_INT, 0);
final double x = wrapper.get(Types.DOUBLE, 0);
final double y = wrapper.get(Types.DOUBLE, 1);
final double z = wrapper.get(Types.DOUBLE, 2);
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
tracker.setBossPosition(entityId, new Vector3d(x, y, z));
});
map(Types.ENTITY_DATA_LIST1_8, Types.ENTITY_DATA_LIST1_9);
handler(wrapper -> {
List<EntityData> entityDataList = wrapper.get(Types.ENTITY_DATA_LIST1_9, 0);
@@ -26,6 +26,7 @@ import com.viaversion.viaversion.api.legacy.bossbar.BossColor;
import com.viaversion.viaversion.api.legacy.bossbar.BossStyle;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.minecraft.GameMode;
import com.viaversion.viaversion.api.minecraft.Vector3d;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_9.EntityType;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_9;
@@ -58,9 +59,15 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public class EntityTracker1_9 extends EntityTrackerBase {
public static final String WITHER_TRANSLATABLE = "{\"translate\":\"entity.WitherBoss.name\"}";
public static final String DRAGON_TRANSLATABLE = "{\"translate\":\"entity.EnderDragon.name\"}";
// 1.8 Entity#isInRangeToRenderDist: average bounding box edge length * 64
private static final double WITHER_RENDER_DISTANCE = (0.9 + 3.5 + 0.9) / 3 * 64;
private static final double DRAGON_RENDER_DISTANCE = (16.0 + 8.0 + 16.0) / 3 * 64;
private final Int2ObjectMap<UUID> uuidMap = new Int2ObjectOpenHashMap<>();
private final Int2IntMap vehicleMap = new Int2IntOpenHashMap();
private final Int2ObjectMap<BossBar> bossBarMap = new Int2ObjectOpenHashMap<>();
private final Int2ObjectMap<Vector3d> bossPositions = new Int2ObjectOpenHashMap<>();
private BossBar shownBossBar;
private Vector3d playerPosition = Vector3d.ZERO;
private final IntSet validBlocking = new IntOpenHashSet();
private final IntSet knownHolograms = new IntOpenHashSet();
private final Set<BlockPosition> blockInteractions = Collections.newSetFromMap(CacheBuilder.newBuilder()
@@ -143,16 +150,92 @@ public class EntityTracker1_9 extends EntityTrackerBase {
validBlocking.remove(entityId);
knownHolograms.remove(entityId);
BossBar bar = bossBarMap.remove(entityId);
if (bar != null) {
bar.hide();
// Send to provider
Via.getManager().getProviders().get(BossBarProvider.class).handleRemove(user(), bar.getId());
this.bossPositions.remove(entityId);
BossBar bar = this.bossBarMap.remove(entityId);
if (bar != null && bar == this.shownBossBar) {
this.updateShownBossBar();
}
return super.removeEntity(entityId);
}
public void setBossPosition(final int entityId, final Vector3d position) {
final com.viaversion.viaversion.api.minecraft.entities.EntityType type = this.entityType(entityId);
if (type != EntityType.WITHER && type != EntityType.ENDER_DRAGON) {
return;
}
this.bossPositions.put(entityId, position);
this.updateShownBossBar();
}
public void moveBoss(final int entityId, final double deltaX, final double deltaY, final double deltaZ) {
final Vector3d position = this.bossPositions.get(entityId);
if (position != null) {
this.setBossPosition(entityId, new Vector3d(position.x() + deltaX, position.y() + deltaY, position.z() + deltaZ));
}
}
public Vector3d getPlayerPosition() {
return this.playerPosition;
}
public void setPlayerPosition(final Vector3d playerPosition) {
this.playerPosition = playerPosition;
this.updateShownBossBar();
}
private void updateShownBossBar() {
BossBar best = null;
int bestSectionDistance = -1;
for (final Int2ObjectMap.Entry<BossBar> entry : this.bossBarMap.int2ObjectEntrySet()) {
final Vector3d position = this.bossPositions.get(entry.getIntKey());
if (position == null) {
continue;
}
final double deltaX = position.x() - this.playerPosition.x();
final double deltaY = position.y() - this.playerPosition.y();
final double deltaZ = position.z() - this.playerPosition.z();
final double range = this.entityType(entry.getIntKey()) == EntityType.ENDER_DRAGON ? DRAGON_RENDER_DISTANCE : WITHER_RENDER_DISTANCE;
if (deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ >= range * range) {
continue;
}
final int distanceX = Math.abs(sectionCoord(position.x()) - sectionCoord(this.playerPosition.x()));
final int distanceY = Math.abs(sectionCoord(position.y()) - sectionCoord(this.playerPosition.y()));
final int distanceZ = Math.abs(sectionCoord(position.z()) - sectionCoord(this.playerPosition.z()));
final int sectionDistance = distanceX + distanceY + distanceZ;
// Prefer the currently shown bar on ties to avoid needless switching
if (sectionDistance > bestSectionDistance || (sectionDistance == bestSectionDistance && entry.getValue() == shownBossBar)) {
best = entry.getValue();
bestSectionDistance = sectionDistance;
}
}
this.showBossBar(best);
}
private static int sectionCoord(double coord) {
return (int) Math.floor(coord) >> 4;
}
private void showBossBar(@Nullable final BossBar bar) {
if (bar == this.shownBossBar) {
return;
}
final BossBarProvider provider = Via.getManager().getProviders().get(BossBarProvider.class);
if (this.shownBossBar != null) {
this.shownBossBar.removeConnection(user());
provider.handleRemove(user(), this.shownBossBar.getId());
}
this.shownBossBar = bar;
if (bar != null) {
bar.addConnection(user());
provider.handleAdd(user(), bar.getId());
}
}
public boolean interactedBlockRecently(final int x, final int y, final int z) {
for (final BlockPosition position : blockInteractions) {
if (Math.abs(position.x() - x) <= 1 && Math.abs(position.y() - y) <= 1 && Math.abs(position.z() - z) <= 1) {
@@ -253,11 +336,7 @@ public class EntityTracker1_9 extends EntityTrackerBase {
if (bar == null) {
bar = Via.getAPI().legacyAPI().createLegacyBossBar(title, BossColor.PINK, BossStyle.SOLID);
bossBarMap.put(entityId, bar);
bar.addConnection(user());
bar.show();
// Send to provider
Via.getManager().getProviders().get(BossBarProvider.class).handleAdd(user(), bar.getId());
updateShownBossBar();
} else {
bar.setTitle(title);
}
@@ -270,10 +349,7 @@ public class EntityTracker1_9 extends EntityTrackerBase {
String title = type == EntityType.ENDER_DRAGON ? DRAGON_TRANSLATABLE : WITHER_TRANSLATABLE;
bar = Via.getAPI().legacyAPI().createLegacyBossBar(title, health, BossColor.PINK, BossStyle.SOLID);
bossBarMap.put(entityId, bar);
bar.addConnection(user());
bar.show();
// Send to provider
Via.getManager().getProviders().get(BossBarProvider.class).handleAdd(user(), bar.getId());
updateShownBossBar();
} else {
bar.setHealth(health);
}