Skip to content

Commit 738dce1

Browse files
committed
Handle cushion color updates in 26.3->26.2
Signed-off-by: Florian Reuth <git@florianreuth.de>
1 parent 2aec244 commit 738dce1

4 files changed

Lines changed: 225 additions & 8 deletions

File tree

‎common/src/main/java/com/viaversion/viabackwards/protocol/v26_3to26_2/Protocol26_3To26_2.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ public void handleArgument(final PacketWrapper wrapper, final String argumentTyp
170170
translatableRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Target Name
171171
});
172172

173+
registerClientbound(ClientboundPackets26_3.BUNDLE_DELIMITER, wrapper -> {
174+
final ProtocolStorables26_3 storables = wrapper.user().storables(this);
175+
storables.bundleStateTracker().toggleBundling();
176+
});
177+
173178
cancelClientbound(ClientboundPackets26_3.ADD_TRANSIENT_BLOCK);
174179
cancelClientbound(ClientboundPackets26_3.POST_EFFECTS);
175180
cancelClientbound(ClientboundConfigurationPackets26_3.POST_EFFECTS);

‎common/src/main/java/com/viaversion/viabackwards/protocol/v26_3to26_2/rewriter/EntityPacketRewriter26_3.java‎

Lines changed: 138 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919

2020
import com.viaversion.viabackwards.api.rewriters.EntityRewriter;
2121
import com.viaversion.viabackwards.protocol.v26_3to26_2.Protocol26_3To26_2;
22+
import com.viaversion.viabackwards.protocol.v26_3to26_2.storage.CushionData;
2223
import com.viaversion.viabackwards.protocol.v26_3to26_2.storage.ProtocolStorables26_3;
24+
import com.viaversion.viaversion.api.connection.UserConnection;
25+
import com.viaversion.viaversion.api.minecraft.Vector3d;
2326
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
2427
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes26_3;
2528
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
@@ -34,15 +37,22 @@
3437
import com.viaversion.viaversion.protocols.v26_2to26_3.packet.ClientboundPacket26_3;
3538
import com.viaversion.viaversion.protocols.v26_2to26_3.packet.ClientboundPackets26_3;
3639
import com.viaversion.viaversion.protocols.v26_2to26_3.packet.ServerboundPackets26_3;
40+
import com.viaversion.viaversion.rewriter.entitydata.EntityDataHandlerEvent;
41+
import com.viaversion.viaversion.util.Copyable;
3742
import java.util.ArrayList;
3843
import java.util.List;
44+
import java.util.UUID;
45+
import org.checkerframework.checker.nullness.qual.Nullable;
3946

4047
public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPacket26_3, Protocol26_3To26_2> {
4148

4249
private static final EntityDataTypes26_1 MAPPED_DATA_TYPES = VersionedTypes.V26_2.entityDataTypes;
4350
private static final int NO_SWING_ANIMATION = 0;
4451
private static final int CHANGE_DESTROY_DIRECTION_ACTION = 1;
4552
private static final int WHITE_CARPET_BLOCK_STATE = 12896;
53+
private static final int RELATIVE_X = 1;
54+
private static final int RELATIVE_Y = 1 << 1;
55+
private static final int RELATIVE_Z = 1 << 2;
4656

4757
public EntityPacketRewriter26_3(final Protocol26_3To26_2 protocol) {
4858
super(protocol, MAPPED_DATA_TYPES.optionalComponentType, MAPPED_DATA_TYPES.booleanType);
@@ -51,20 +61,22 @@ public EntityPacketRewriter26_3(final Protocol26_3To26_2 protocol) {
5161
@Override
5262
public void registerPackets() {
5363
protocol.registerClientbound(ClientboundPackets26_3.MOVE_ENTITY_POS, wrapper -> {
54-
wrapper.passthrough(Types.VAR_INT); // Entity ID
64+
final int entityId = wrapper.passthrough(Types.VAR_INT);
5565

5666
final int properties = wrapper.read(Types.VAR_INT);
5767
final boolean onGround = (properties & 1) != 0;
5868
handleMovePos(wrapper, properties);
69+
trackCushionMovement(wrapper, entityId);
5970
wrapper.write(Types.BOOLEAN, onGround);
6071
});
6172

6273
protocol.registerClientbound(ClientboundPackets26_3.MOVE_ENTITY_POS_ROT, wrapper -> {
63-
wrapper.passthrough(Types.VAR_INT); // Entity ID
74+
final int entityId = wrapper.passthrough(Types.VAR_INT);
6475

6576
final int properties = wrapper.read(Types.VAR_INT);
6677
final boolean onGround = (properties & 1) != 0;
6778
handleMovePos(wrapper, properties);
79+
trackCushionMovement(wrapper, entityId);
6880

6981
wrapper.passthrough(Types.BYTE); // Y rot
7082
wrapper.passthrough(Types.BYTE); // X rot
@@ -81,16 +93,17 @@ public void registerPackets() {
8193
});
8294

8395
protocol.registerClientbound(ClientboundPackets26_3.ENTITY_POSITION_SYNC, wrapper -> {
84-
wrapper.passthrough(Types.VAR_INT); // Entity ID
96+
final int entityId = wrapper.passthrough(Types.VAR_INT);
8597

8698
final int pathType = wrapper.read(Types.VAR_INT);
8799
if (pathType == 0) { // Linear
88-
wrapper.passthrough(Types.DOUBLE); // X
89-
wrapper.passthrough(Types.DOUBLE); // Y
90-
wrapper.passthrough(Types.DOUBLE); // Z
100+
final double x = wrapper.passthrough(Types.DOUBLE);
101+
final double y = wrapper.passthrough(Types.DOUBLE);
102+
final double z = wrapper.passthrough(Types.DOUBLE);
91103
wrapper.write(Types.DOUBLE, 0D); // Delta x
92104
wrapper.write(Types.DOUBLE, 0D); // Delta y
93105
wrapper.write(Types.DOUBLE, 0D); // Delta z
106+
trackCushionPosition(wrapper, entityId, x, y, z);
94107
return;
95108
}
96109

@@ -116,6 +129,7 @@ public void registerPackets() {
116129
wrapper.write(Types.DOUBLE, x);
117130
wrapper.write(Types.DOUBLE, y);
118131
wrapper.write(Types.DOUBLE, z);
132+
trackCushionPosition(wrapper, entityId, x, y, z);
119133
if (steps > 1 && tickOffset > 0) {
120134
wrapper.write(Types.DOUBLE, (x - previousX) / tickOffset); // Delta x
121135
wrapper.write(Types.DOUBLE, (y - previousY) / tickOffset); // Delta y
@@ -127,6 +141,28 @@ public void registerPackets() {
127141
}
128142
});
129143

144+
protocol.registerClientbound(ClientboundPackets26_3.TELEPORT_ENTITY, wrapper -> {
145+
final int entityId = wrapper.passthrough(Types.VAR_INT);
146+
final CushionData cushionData = cushionData(wrapper, entityId);
147+
if (cushionData == null) {
148+
return;
149+
}
150+
151+
final double x = wrapper.passthrough(Types.DOUBLE);
152+
final double y = wrapper.passthrough(Types.DOUBLE);
153+
final double z = wrapper.passthrough(Types.DOUBLE);
154+
wrapper.passthrough(Types.DOUBLE); // Delta x
155+
wrapper.passthrough(Types.DOUBLE); // Delta y
156+
wrapper.passthrough(Types.DOUBLE); // Delta z
157+
wrapper.passthrough(Types.FLOAT); // Y rot
158+
wrapper.passthrough(Types.FLOAT); // X rot
159+
final int relatives = wrapper.passthrough(Types.INT);
160+
final double newX = (relatives & RELATIVE_X) != 0 ? cushionData.x() + x : x;
161+
final double newY = (relatives & RELATIVE_Y) != 0 ? cushionData.y() + y : y;
162+
final double newZ = (relatives & RELATIVE_Z) != 0 ? cushionData.z() + z : z;
163+
cushionData.setPosition(newX, newY, newZ);
164+
});
165+
130166
protocol.appendClientbound(ClientboundPackets26_3.LOGIN, wrapper -> {
131167
wrapper.rewindReader(1);
132168
handleGamemodes(wrapper);
@@ -204,13 +240,19 @@ public void registerPackets() {
204240
return;
205241
}
206242

207-
// Set falling block data to white carpet block state
243+
// Set falling block data to white carpet block state, the actual color follows in the entity data
208244
wrapper.set(Types.VAR_INT, 2, WHITE_CARPET_BLOCK_STATE);
245+
final UUID uuid = wrapper.get(Types.UUID, 0);
246+
final double x = wrapper.get(Types.DOUBLE, 0);
247+
final double y = wrapper.get(Types.DOUBLE, 1);
248+
final double z = wrapper.get(Types.DOUBLE, 2);
249+
final CushionData cushionData = new CushionData(uuid, x, y, z);
250+
cushionData.entityData().add(new EntityData(5, MAPPED_DATA_TYPES.booleanType, true)); // No gravity
251+
entity.put(cushionData);
209252

210253
wrapper.send(Protocol26_3To26_2.class);
211254
wrapper.cancel();
212255

213-
// Send no gravity entity data
214256
final List<EntityData> entityDataList = new ArrayList<>();
215257
entityDataList.add(new EntityData(5, MAPPED_DATA_TYPES.booleanType, true)); // No gravity
216258

@@ -221,6 +263,91 @@ public void registerPackets() {
221263
});
222264
}
223265

266+
private void updateCushionColor(final EntityDataHandlerEvent event, final int color) {
267+
final CushionData cushionData = event.trackedEntity().get(CushionData.class);
268+
if (cushionData == null || cushionData.color() == color) {
269+
return;
270+
}
271+
272+
cushionData.setColor(color);
273+
274+
final ProtocolStorables26_3 storables = event.user().storables(protocol);
275+
final boolean isBundling = storables.bundleStateTracker().isBundling();
276+
if (!isBundling) {
277+
final PacketWrapper bundleStart = PacketWrapper.create(ClientboundPackets26_1.BUNDLE_DELIMITER, event.user());
278+
bundleStart.send(Protocol26_3To26_2.class);
279+
}
280+
281+
// Falling blocks can't change their block state after spawning, so respawn it with the colored carpet
282+
final PacketWrapper removeEntityPacket = PacketWrapper.create(ClientboundPackets26_1.REMOVE_ENTITIES, event.user());
283+
removeEntityPacket.write(Types.VAR_INT_ARRAY_PRIMITIVE, new int[]{event.entityId()});
284+
removeEntityPacket.send(Protocol26_3To26_2.class);
285+
286+
final PacketWrapper addEntityPacket = PacketWrapper.create(ClientboundPackets26_1.ADD_ENTITY, event.user());
287+
addEntityPacket.write(Types.VAR_INT, event.entityId());
288+
addEntityPacket.write(Types.UUID, cushionData.uuid());
289+
addEntityPacket.write(Types.VAR_INT, newEntityId(fallingBlockType().getId()));
290+
addEntityPacket.write(Types.DOUBLE, cushionData.x());
291+
addEntityPacket.write(Types.DOUBLE, cushionData.y());
292+
addEntityPacket.write(Types.DOUBLE, cushionData.z());
293+
addEntityPacket.write(Types.LOW_PRECISION_VECTOR, Vector3d.ZERO); // Movement
294+
addEntityPacket.write(Types.BYTE, (byte) 0); // X rot, unused by falling blocks
295+
addEntityPacket.write(Types.BYTE, (byte) 0); // Y rot
296+
addEntityPacket.write(Types.BYTE, (byte) 0); // Y head rot
297+
addEntityPacket.write(Types.VAR_INT, WHITE_CARPET_BLOCK_STATE + color);
298+
addEntityPacket.send(Protocol26_3To26_2.class);
299+
300+
final PacketWrapper entityDataPacket = PacketWrapper.create(ClientboundPackets26_1.SET_ENTITY_DATA, event.user());
301+
entityDataPacket.write(Types.VAR_INT, event.entityId());
302+
entityDataPacket.write(VersionedTypes.V26_2.entityDataList, cushionData.entityData());
303+
entityDataPacket.send(Protocol26_3To26_2.class);
304+
305+
if (!isBundling) {
306+
final PacketWrapper bundleEnd = PacketWrapper.create(ClientboundPackets26_1.BUNDLE_DELIMITER, event.user());
307+
bundleEnd.send(Protocol26_3To26_2.class);
308+
}
309+
}
310+
311+
@Override
312+
public void handleEntityData(final int entityId, final List<EntityData> dataList, final UserConnection connection) {
313+
super.handleEntityData(entityId, dataList, connection);
314+
315+
final TrackedEntity entity = tracker(connection).entity(entityId);
316+
final CushionData cushionData = entity != null ? entity.get(CushionData.class) : null;
317+
if (cushionData == null) {
318+
return;
319+
}
320+
321+
final List<EntityData> entityData = cushionData.entityData();
322+
entityData.removeIf(first -> dataList.stream().anyMatch(second -> first.id() == second.id()));
323+
for (final EntityData data : dataList) {
324+
final Object value = data.value();
325+
entityData.add(new EntityData(data.id(), data.dataType(), Copyable.copy(value)));
326+
}
327+
}
328+
329+
private @Nullable CushionData cushionData(final PacketWrapper wrapper, final int entityId) {
330+
final TrackedEntity entity = tracker(wrapper.user()).entity(entityId);
331+
return entity != null ? entity.get(CushionData.class) : null;
332+
}
333+
334+
private void trackCushionMovement(final PacketWrapper wrapper, final int entityId) {
335+
final CushionData cushionData = cushionData(wrapper, entityId);
336+
if (cushionData != null) {
337+
final double x = cushionData.x() + wrapper.get(Types.SHORT, 0) / 4096D;
338+
final double y = cushionData.y() + wrapper.get(Types.SHORT, 1) / 4096D;
339+
final double z = cushionData.z() + wrapper.get(Types.SHORT, 2) / 4096D;
340+
cushionData.setPosition(x, y, z);
341+
}
342+
}
343+
344+
private void trackCushionPosition(final PacketWrapper wrapper, final int entityId, final double x, final double y, final double z) {
345+
final CushionData cushionData = cushionData(wrapper, entityId);
346+
if (cushionData != null) {
347+
cushionData.setPosition(x, y, z);
348+
}
349+
}
350+
224351
private void handleGamemodes(final PacketWrapper wrapper) {
225352
final int gamemode = wrapper.read(Types.VAR_INT);
226353
wrapper.write(Types.BYTE, (byte) gamemode);
@@ -267,6 +394,9 @@ protected void registerRewrites() {
267394
MAPPED_DATA_TYPES.optionalComponentType
268395
);
269396
filter().type(EntityTypes26_3.CUSHION).handler((event, data) -> {
397+
if (event.index() == 8) {
398+
updateCushionColor(event, data.value());
399+
}
270400
if (event.index() > 7) {
271401
event.cancel(); // Cancel all non-entity data
272402
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
3+
* Copyright (C) 2016-2026 ViaVersion and contributors
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package com.viaversion.viabackwards.protocol.v26_3to26_2.storage;
19+
20+
import com.viaversion.viaversion.api.connection.StorableObject;
21+
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
import java.util.UUID;
25+
26+
public final class CushionData implements StorableObject {
27+
28+
private final UUID uuid;
29+
private final List<EntityData> entityData = new ArrayList<>();
30+
private double x;
31+
private double y;
32+
private double z;
33+
private int color;
34+
35+
public CushionData(final UUID uuid, final double x, final double y, final double z) {
36+
this.uuid = uuid;
37+
this.x = x;
38+
this.y = y;
39+
this.z = z;
40+
}
41+
42+
public UUID uuid() {
43+
return uuid;
44+
}
45+
46+
public List<EntityData> entityData() {
47+
return entityData;
48+
}
49+
50+
public double x() {
51+
return x;
52+
}
53+
54+
public double y() {
55+
return y;
56+
}
57+
58+
public double z() {
59+
return z;
60+
}
61+
62+
public void setPosition(final double x, final double y, final double z) {
63+
this.x = x;
64+
this.y = y;
65+
this.z = z;
66+
}
67+
68+
public int color() {
69+
return color;
70+
}
71+
72+
public void setColor(final int color) {
73+
this.color = color;
74+
}
75+
76+
}

‎common/src/main/java/com/viaversion/viabackwards/protocol/v26_3to26_2/storage/ProtocolStorables26_3.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,19 @@
1818
package com.viaversion.viabackwards.protocol.v26_3to26_2.storage;
1919

2020
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
21+
import com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.BundleStateTracker;
2122
import org.checkerframework.checker.nullness.qual.Nullable;
2223

2324
public final class ProtocolStorables26_3 extends ProtocolStorablesBase {
2425

26+
private final BundleStateTracker bundleStateTracker = new BundleStateTracker();
2527
private Integer currentTeleportId;
2628
private int brewingStandContainerId = -1;
2729

30+
public BundleStateTracker bundleStateTracker() {
31+
return bundleStateTracker;
32+
}
33+
2834
public @Nullable Integer currentTeleportId() {
2935
return currentTeleportId;
3036
}

0 commit comments

Comments
 (0)