Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,8 @@ public Item handleItemToServer(UserConnection connection, Item item) {
item.setData((short) 0);
}

if (item.identifier() == 373 && (tag == null || !tag.contains("Potion"))) { // Potions
if (item.identifier() == 373) { // Potions
// Restore the splash id even when a Potion tag is present (e.g. items re-sent by a creative mode client)
if (item.data() >= 16384) {
item.setIdentifier(438);
item.setData((short) (item.data() - 8192));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ public void register() {
wrapper.passthrough(Types.SHORT); // Velocity x
wrapper.passthrough(Types.SHORT); // Velocity y
wrapper.passthrough(Types.SHORT); // Velocity z
if (type.is(EntityTypes1_9.EntityType.POTION)) {
// 1.8 clients read the potion type from the object data, but newer versions only
// send it in the entity data - the next packet. Held until then, see handleEntityData
wrapper.cancel();
final EntityTracker1_9 entityTracker = tracker(wrapper.user());
entityTracker.getPendingPotionSpawns().put(entityId, new EntityTracker1_9.PotionSpawn(
wrapper.get(Types.INT, 0), wrapper.get(Types.INT, 1), wrapper.get(Types.INT, 2),
wrapper.get(Types.BYTE, 1), wrapper.get(Types.BYTE, 2),
wrapper.get(Types.SHORT, 0), wrapper.get(Types.SHORT, 1), wrapper.get(Types.SHORT, 2)));
}
} else {
final short velocityX = wrapper.read(Types.SHORT);
final short velocityY = wrapper.read(Types.SHORT);
Expand Down Expand Up @@ -541,8 +551,40 @@ protected void registerRewrites() {
filter().handler(this::handleEntityData);
}

private void sendDelayedPotionSpawn(EntityDataHandlerEvent event, EntityTracker1_9 tracker, Item item) {
final EntityTracker1_9.PotionSpawn spawn = tracker.getPendingPotionSpawns().remove(event.entityId());
if (spawn == null) {
return;
}
final Item converted = protocol.getItemRewriter().handleItemToClient(event.user(), item);
short data = converted == null ? 0 : converted.data();
if (data <= 0) {
data = 16384; // Unmapped potion, at least display a splash bottle
}

final PacketWrapper addEntity = PacketWrapper.create(ClientboundPackets1_8.ADD_ENTITY, event.user());
addEntity.write(Types.VAR_INT, event.entityId());
addEntity.write(Types.BYTE, (byte) EntityTypes1_9.ObjectType.POTION.getId());
addEntity.write(Types.INT, spawn.x());
addEntity.write(Types.INT, spawn.y());
addEntity.write(Types.INT, spawn.z());
addEntity.write(Types.BYTE, spawn.pitch());
addEntity.write(Types.BYTE, spawn.yaw());
addEntity.write(Types.INT, (int) data);
addEntity.write(Types.SHORT, spawn.velocityX());
addEntity.write(Types.SHORT, spawn.velocityY());
addEntity.write(Types.SHORT, spawn.velocityZ());
addEntity.send(Protocol1_9To1_8.class); // Before the entity data is sent
}

private void handleEntityData(EntityDataHandlerEvent event, EntityData entityData) {
final EntityTracker1_9 tracker = tracker(event.user());
if (event.entityType() == EntityTypes1_9.EntityType.POTION && entityData.value() instanceof Item potionItem) {
// No 1.8 equivalent; supplies the object data of the held-back spawn. Matched by value type as the index is version dependent
sendDelayedPotionSpawn(event, tracker, potionItem);
event.cancel();
return;
}
if (entityData.id() == EntityDataIndex1_9.ENTITY_STATUS.getIndex()) {
tracker.getStatus().put(event.entityId(), (Byte) entityData.value());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class EntityTracker1_9 extends EntityTrackerBase {
private final Int2ObjectMap<IntList> vehicles = new Int2ObjectOpenHashMap<>();
private final Int2ObjectMap<Vector> offsets = new Int2ObjectOpenHashMap<>();
private final Int2IntMap status = new Int2IntOpenHashMap();
private final Int2ObjectMap<PotionSpawn> pendingPotionSpawns = new Int2ObjectOpenHashMap<>();

public EntityTracker1_9(UserConnection connection) {
super(connection, EntityTypes1_9.EntityType.PLAYER);
Expand All @@ -45,6 +46,7 @@ public void removeEntity(int id) {
vehicles.remove(id);
offsets.remove(id);
status.remove(id);
pendingPotionSpawns.remove(id);

vehicles.forEach((vehicle, passengers) -> passengers.rem(id));
vehicles.int2ObjectEntrySet().removeIf(entry -> entry.getValue().isEmpty());
Expand Down Expand Up @@ -88,4 +90,12 @@ public Integer getVehicle(final int passenger) {
public Int2IntMap getStatus() {
return status;
}

public Int2ObjectMap<PotionSpawn> getPendingPotionSpawns() {
return pendingPotionSpawns;
}

/** Spawn packet values of a thrown potion, held until its item entity data supplies the 1.8 object data. */
public record PotionSpawn(int x, int y, int z, byte pitch, byte yaw, short velocityX, short velocityY, short velocityZ) {
}
}