Skip to content

Commit f2426b3

Browse files
committed
Map BLOCK_CHANGED_ACK to BLOCK_BREAK_ACK in 1.19 -> 1.18.2
1 parent 5361c01 commit f2426b3

4 files changed

Lines changed: 239 additions & 18 deletions

File tree

common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/Protocol1_19To1_18_2.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.rewriter.BlockItemPacketRewriter1_19;
2828
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.rewriter.CommandRewriter1_19;
2929
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.rewriter.EntityPacketRewriter1_19;
30+
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.BlockAckStorage;
3031
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.DimensionRegistryStorage;
3132
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.EntityTracker1_19;
3233
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.NonceStorage;
@@ -343,6 +344,7 @@ public void register() {
343344

344345
@Override
345346
public void init(final UserConnection user) {
347+
user.put(new BlockAckStorage());
346348
user.put(new DimensionRegistryStorage());
347349
addEntityTracker(user, new EntityTracker1_19(user));
348350
}

common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/rewriter/BlockItemPacketRewriter1_19.java

Lines changed: 106 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@
2121
import com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;
2222
import com.viaversion.viabackwards.api.rewriters.EnchantmentRewriter;
2323
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.Protocol1_19To1_18_2;
24+
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.BlockAckStorage;
2425
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.LastDeathPosition;
2526
import com.viaversion.viaversion.api.connection.UserConnection;
2627
import com.viaversion.viaversion.api.data.ParticleMappings;
2728
import com.viaversion.viaversion.api.data.entity.EntityTracker;
29+
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
30+
import com.viaversion.viaversion.api.minecraft.BlockPosition;
2831
import com.viaversion.viaversion.api.minecraft.GlobalBlockPosition;
2932
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
3033
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
@@ -36,6 +39,7 @@
3639
import com.viaversion.viaversion.api.type.Types;
3740
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_18;
3841
import com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;
42+
import com.viaversion.viaversion.protocols.v1_17_1to1_18.packet.ClientboundPackets1_18;
3943
import com.viaversion.viaversion.protocols.v1_18_2to1_19.packet.ClientboundPackets1_19;
4044
import com.viaversion.viaversion.rewriter.RecipeRewriter;
4145
import com.viaversion.viaversion.util.MathUtil;
@@ -84,14 +88,88 @@ public void register() {
8488
}
8589
});
8690

87-
protocol.registerClientbound(ClientboundPackets1_19.BLOCK_CHANGED_ACK, null, new PacketHandlers() {
91+
protocol.registerClientbound(ClientboundPackets1_19.BLOCK_CHANGED_ACK, ClientboundPackets1_18.BLOCK_BREAK_ACK, new PacketHandlers() {
8892
@Override
8993
public void register() {
90-
read(Types.VAR_INT); // Sequence
91-
handler(PacketWrapper::cancel); // This is fine:tm:
94+
handler(wrapper -> {
95+
final BlockAckStorage storage = wrapper.user().get(BlockAckStorage.class);
96+
final int sequence = wrapper.read(Types.VAR_INT);
97+
final BlockAckStorage.BlockAction action = storage.remove(sequence);
98+
if (action == null) {
99+
wrapper.cancel();
100+
return;
101+
}
102+
final BlockPosition pos = action.position();
103+
104+
final int minSectionY = protocol.getEntityRewriter().tracker(wrapper.user()).currentMinY() >> 4;
105+
final int blockState = storage.getBlockStateAt(pos, minSectionY);
106+
if (blockState < 0) {
107+
wrapper.cancel();
108+
return;
109+
}
110+
111+
wrapper.write(Types.BLOCK_POSITION1_14, pos);
112+
wrapper.write(Types.VAR_INT, blockState);
113+
wrapper.write(Types.VAR_INT, (int) action.action());
114+
wrapper.write(Types.BOOLEAN, false);
115+
});
116+
}
117+
});
118+
119+
protocol.replaceClientbound(ClientboundPackets1_19.BLOCK_UPDATE, new PacketHandlers() {
120+
@Override
121+
public void register() {
122+
handler(wrapper -> {
123+
final BlockPosition pos = wrapper.passthrough(Types.BLOCK_POSITION1_14);
124+
final int blockId = wrapper.read(Types.VAR_INT);
125+
final int mappedId = protocol.getMappingData().getNewBlockStateId(blockId);
126+
wrapper.write(Types.VAR_INT, mappedId);
127+
final int minSectionY = protocol.getEntityRewriter().tracker(wrapper.user()).currentMinY() >> 4;
128+
wrapper.user().get(BlockAckStorage.class).updateBlockState(pos.x(), pos.y(), pos.z(), minSectionY, mappedId);
129+
});
130+
}
131+
});
132+
133+
protocol.replaceClientbound(ClientboundPackets1_19.SECTION_BLOCKS_UPDATE, new PacketHandlers() {
134+
@Override
135+
public void register() {
136+
handler(wrapper -> {
137+
final long sectionPos = wrapper.passthrough(Types.LONG);
138+
wrapper.passthrough(Types.BOOLEAN); // Suppress light updates
139+
140+
final int sectionY = (int) ((sectionPos << 44) >> 44);
141+
final int chunkZ = (int) ((sectionPos << 22) >> 42);
142+
final int chunkX = (int) (sectionPos >> 42);
143+
144+
final int minSectionY = protocol.getEntityRewriter().tracker(wrapper.user()).currentMinY() >> 4;
145+
final BlockAckStorage storage = wrapper.user().get(BlockAckStorage.class);
146+
for (final BlockChangeRecord record : wrapper.passthrough(Types.VAR_LONG_BLOCK_CHANGE_ARRAY)) {
147+
final int mappedId = protocol.getMappingData().getNewBlockStateId(record.getBlockId());
148+
record.setBlockId(mappedId);
149+
storage.updateBlockState(
150+
(chunkX << 4) | record.getSectionX(),
151+
(sectionY << 4) + record.getSectionY(),
152+
(chunkZ << 4) | record.getSectionZ(),
153+
minSectionY,
154+
mappedId
155+
);
156+
}
157+
});
92158
}
93159
});
94160

161+
protocol.replaceClientbound(ClientboundPackets1_19.LEVEL_CHUNK_WITH_LIGHT, wrapper -> {
162+
final Chunk chunk = protocol.getBlockRewriter().handleChunk1_18(wrapper);
163+
protocol.getBlockRewriter().handleBlockEntities(chunk, wrapper.user());
164+
wrapper.user().get(BlockAckStorage.class).cacheChunk(chunk.getX(), chunk.getZ(), chunk.getSections());
165+
});
166+
167+
protocol.registerClientbound(ClientboundPackets1_19.FORGET_LEVEL_CHUNK, wrapper -> {
168+
final int chunkX = wrapper.passthrough(Types.INT);
169+
final int chunkZ = wrapper.passthrough(Types.INT);
170+
wrapper.user().get(BlockAckStorage.class).forgetChunk(chunkX, chunkZ);
171+
});
172+
95173
protocol.replaceClientbound(ClientboundPackets1_19.LEVEL_PARTICLES, new PacketHandlers() {
96174
@Override
97175
public void register() {
@@ -126,34 +204,44 @@ public void register() {
126204
}
127205
});
128206

129-
// The server does nothing but track the sequence, so we can just set it as 0
130207
protocol.registerServerbound(ServerboundPackets1_17.PLAYER_ACTION, new PacketHandlers() {
131208
@Override
132209
public void register() {
133-
map(Types.VAR_INT); // Action
134-
map(Types.BLOCK_POSITION1_14); // Block position
135-
map(Types.UNSIGNED_BYTE); // Direction
136-
create(Types.VAR_INT, 0); // Sequence
210+
handler(wrapper -> {
211+
final BlockAckStorage storage = wrapper.user().get(BlockAckStorage.class);
212+
final int action = wrapper.passthrough(Types.VAR_INT);
213+
final BlockPosition pos = wrapper.passthrough(Types.BLOCK_POSITION1_14);
214+
wrapper.passthrough(Types.UNSIGNED_BYTE); // Direction
215+
final int sequence = storage.nextSequence();
216+
wrapper.write(Types.VAR_INT, sequence); // Sequence
217+
if (action < 3) {
218+
storage.add(sequence, pos, action);
219+
}
220+
});
137221
}
138222
});
139223
protocol.registerServerbound(ServerboundPackets1_17.USE_ITEM_ON, new PacketHandlers() {
140224
@Override
141225
public void register() {
142-
map(Types.VAR_INT); // Hand
143-
map(Types.BLOCK_POSITION1_14); // Block position
144-
map(Types.VAR_INT); // Direction
145-
map(Types.FLOAT); // X
146-
map(Types.FLOAT); // Y
147-
map(Types.FLOAT); // Z
148-
map(Types.BOOLEAN); // Inside
149-
create(Types.VAR_INT, 0); // Sequence
226+
handler(wrapper -> {
227+
wrapper.passthrough(Types.VAR_INT); // Hand
228+
wrapper.passthrough(Types.BLOCK_POSITION1_14); // Block position
229+
wrapper.passthrough(Types.VAR_INT); // Direction
230+
wrapper.passthrough(Types.FLOAT); // X
231+
wrapper.passthrough(Types.FLOAT); // Y
232+
wrapper.passthrough(Types.FLOAT); // Z
233+
wrapper.passthrough(Types.BOOLEAN); // Inside
234+
wrapper.write(Types.VAR_INT, wrapper.user().get(BlockAckStorage.class).nextSequence()); // Sequence
235+
});
150236
}
151237
});
152238
protocol.registerServerbound(ServerboundPackets1_17.USE_ITEM, new PacketHandlers() {
153239
@Override
154240
public void register() {
155-
map(Types.VAR_INT); // Hand
156-
create(Types.VAR_INT, 0); // Sequence
241+
handler(wrapper -> {
242+
wrapper.passthrough(Types.VAR_INT); // Hand
243+
wrapper.write(Types.VAR_INT, wrapper.user().get(BlockAckStorage.class).nextSequence()); // Sequence
244+
});
157245
}
158246
});
159247

common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/rewriter/EntityPacketRewriter1_19.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.viaversion.viabackwards.ViaBackwards;
2424
import com.viaversion.viabackwards.api.rewriters.EntityRewriter;
2525
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.Protocol1_19To1_18_2;
26+
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.BlockAckStorage;
2627
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.DimensionRegistryStorage;
2728
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.EntityTracker1_19;
2829
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.LastDeathPosition;
@@ -208,6 +209,7 @@ public void register() {
208209
handler(wrapper -> {
209210
final DimensionRegistryStorage dimensionRegistryStorage = wrapper.user().get(DimensionRegistryStorage.class);
210211
dimensionRegistryStorage.clear();
212+
wrapper.user().get(BlockAckStorage.class).clear();
211213

212214
// Cache dimensions and find current dimension
213215
final String dimensionKey = Key.stripMinecraftNamespace(wrapper.read(Types.STRING));
@@ -269,6 +271,7 @@ public void register() {
269271
@Override
270272
public void register() {
271273
handler(wrapper -> {
274+
wrapper.user().get(BlockAckStorage.class).clear();
272275
final String dimensionKey = wrapper.read(Types.STRING);
273276
final CompoundTag dimension = wrapper.user().get(DimensionRegistryStorage.class).dimension(dimensionKey);
274277
if (dimension == null) {
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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.v1_19to1_18_2.storage;
19+
20+
import com.viaversion.viaversion.api.connection.StorableObject;
21+
import com.viaversion.viaversion.api.minecraft.BlockPosition;
22+
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
23+
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
24+
import com.viaversion.viaversion.api.minecraft.chunks.DataPaletteImpl;
25+
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
26+
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectLinkedOpenHashMap;
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
import org.checkerframework.checker.nullness.qual.Nullable;
30+
31+
public final class BlockAckStorage implements StorableObject {
32+
33+
private final Int2ObjectLinkedOpenHashMap<BlockAction> actions = new Int2ObjectLinkedOpenHashMap<>();
34+
private final Map<Long, DataPalette[]> cachedChunks = new HashMap<>();
35+
private int sequenceId;
36+
37+
public int nextSequence() {
38+
return sequenceId++;
39+
}
40+
41+
public void add(final int sequence, final BlockPosition position, final int action) {
42+
actions.put(sequence, new BlockAction(position, (byte) action));
43+
44+
// Some actions may be left unacknowledged by modded servers
45+
if (actions.size() > 100) {
46+
actions.removeFirst();
47+
}
48+
}
49+
50+
public @Nullable BlockAction remove(final int sequence) {
51+
return actions.remove(sequence);
52+
}
53+
54+
public void cacheChunk(final int chunkX, final int chunkZ, final ChunkSection[] sections) {
55+
final DataPalette[] blockPalettes = new DataPalette[sections.length];
56+
for (int i = 0; i < sections.length; i++) {
57+
final ChunkSection section = sections[i];
58+
if (section == null) {
59+
continue;
60+
}
61+
62+
final DataPalette palette = section.palette(PaletteType.BLOCKS);
63+
if (palette != null) {
64+
blockPalettes[i] = copyPalette(palette);
65+
}
66+
}
67+
cachedChunks.put(packChunk(chunkX, chunkZ), blockPalettes);
68+
}
69+
70+
public void forgetChunk(final int chunkX, final int chunkZ) {
71+
cachedChunks.remove(packChunk(chunkX, chunkZ));
72+
}
73+
74+
public void clear() {
75+
actions.clear();
76+
cachedChunks.clear();
77+
sequenceId = 0;
78+
}
79+
80+
public void updateBlockState(final int blockX, final int blockY, final int blockZ, final int minSectionY, final int state) {
81+
final DataPalette[] sections = cachedChunks.get(packChunk(blockX >> 4, blockZ >> 4));
82+
if (sections == null) {
83+
return;
84+
}
85+
final int sectionIdx = (blockY >> 4) - minSectionY;
86+
if (sectionIdx < 0 || sectionIdx >= sections.length) {
87+
return;
88+
}
89+
final DataPalette palette = sections[sectionIdx];
90+
if (palette == null) {
91+
return;
92+
}
93+
palette.setIdAt(blockX & 15, blockY & 15, blockZ & 15, state);
94+
}
95+
96+
public int getBlockStateAt(final BlockPosition pos, final int minSectionY) {
97+
final DataPalette[] sections = cachedChunks.get(packChunk(pos.x() >> 4, pos.z() >> 4));
98+
if (sections == null) {
99+
return -1;
100+
}
101+
final int sectionIdx = (pos.y() >> 4) - minSectionY;
102+
if (sectionIdx < 0 || sectionIdx >= sections.length) {
103+
return -1;
104+
}
105+
final DataPalette palette = sections[sectionIdx];
106+
if (palette == null) {
107+
return -1;
108+
}
109+
return palette.idAt(pos.x() & 15, pos.y() & 15, pos.z() & 15);
110+
}
111+
112+
private static DataPalette copyPalette(final DataPalette palette) {
113+
final DataPaletteImpl copy = new DataPaletteImpl(ChunkSection.SIZE, palette.size());
114+
for (int i = 0; i < palette.size(); i++) {
115+
copy.addId(palette.idByIndex(i));
116+
}
117+
for (int i = 0; i < ChunkSection.SIZE; i++) {
118+
copy.setPaletteIndexAt(i, palette.paletteIndexAt(i));
119+
}
120+
return copy;
121+
}
122+
123+
private static long packChunk(final int x, final int z) {
124+
return ((long) x << 32) | (z & 0xFFFFFFFFL);
125+
}
126+
127+
public record BlockAction(BlockPosition position, byte action) {}
128+
}

0 commit comments

Comments
 (0)