455 lines
21 KiB
Java
455 lines
21 KiB
Java
/*
|
|
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
|
* Copyright (C) 2016-2024 ViaVersion and contributors
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package com.viaversion.viaversion.protocols.protocol1_20_2to1_20.rewriter;
|
|
|
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|
import com.github.steveice10.opennbt.tag.builtin.IntTag;
|
|
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
|
import com.github.steveice10.opennbt.tag.builtin.NumberTag;
|
|
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
|
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
|
import com.viaversion.viaversion.api.data.ParticleMappings;
|
|
import com.viaversion.viaversion.api.data.entity.EntityTracker;
|
|
import com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;
|
|
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
|
|
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
|
|
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
|
|
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
|
|
import com.viaversion.viaversion.api.minecraft.item.Item;
|
|
import com.viaversion.viaversion.api.minecraft.metadata.ChunkPosition;
|
|
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
|
|
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
|
|
import com.viaversion.viaversion.api.type.Type;
|
|
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_18;
|
|
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;
|
|
import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.ClientboundPackets1_19_4;
|
|
import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.rewriter.RecipeRewriter1_19_4;
|
|
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.Protocol1_20_2To1_20;
|
|
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.packet.ServerboundPackets1_20_2;
|
|
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.util.PotionEffects;
|
|
import com.viaversion.viaversion.rewriter.BlockRewriter;
|
|
import com.viaversion.viaversion.rewriter.ItemRewriter;
|
|
import com.viaversion.viaversion.util.MathUtil;
|
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
|
|
public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<ClientboundPackets1_19_4, ServerboundPackets1_20_2, Protocol1_20_2To1_20> {
|
|
|
|
public BlockItemPacketRewriter1_20_2(final Protocol1_20_2To1_20 protocol) {
|
|
super(protocol, Type.ITEM1_13_2, Type.ITEM1_13_2_ARRAY);
|
|
}
|
|
|
|
@Override
|
|
public void registerPackets() {
|
|
final BlockRewriter<ClientboundPackets1_19_4> blockRewriter = BlockRewriter.for1_14(protocol);
|
|
blockRewriter.registerBlockAction(ClientboundPackets1_19_4.BLOCK_ACTION);
|
|
blockRewriter.registerBlockChange(ClientboundPackets1_19_4.BLOCK_CHANGE);
|
|
blockRewriter.registerVarLongMultiBlockChange1_20(ClientboundPackets1_19_4.MULTI_BLOCK_CHANGE);
|
|
blockRewriter.registerEffect(ClientboundPackets1_19_4.EFFECT, 1010, 2001);
|
|
|
|
protocol.registerServerbound(ServerboundPackets1_20_2.SET_BEACON_EFFECT, wrapper -> {
|
|
// Effects start at 1 before 1.20.2
|
|
if (wrapper.passthrough(Type.BOOLEAN)) { // Primary effect
|
|
wrapper.write(Type.VAR_INT, wrapper.read(Type.VAR_INT) + 1);
|
|
}
|
|
if (wrapper.passthrough(Type.BOOLEAN)) { // Secondary effect
|
|
wrapper.write(Type.VAR_INT, wrapper.read(Type.VAR_INT) + 1);
|
|
}
|
|
});
|
|
|
|
protocol.registerClientbound(ClientboundPackets1_19_4.UNLOAD_CHUNK, wrapper -> {
|
|
final int x = wrapper.read(Type.INT);
|
|
final int z = wrapper.read(Type.INT);
|
|
wrapper.write(Type.CHUNK_POSITION, new ChunkPosition(x, z));
|
|
});
|
|
|
|
protocol.registerClientbound(ClientboundPackets1_19_4.NBT_QUERY, wrapper -> {
|
|
wrapper.passthrough(Type.VAR_INT); // Transaction id
|
|
wrapper.write(Type.COMPOUND_TAG, wrapper.read(Type.NAMED_COMPOUND_TAG));
|
|
});
|
|
|
|
protocol.registerClientbound(ClientboundPackets1_19_4.BLOCK_ENTITY_DATA, wrapper -> {
|
|
wrapper.passthrough(Type.POSITION1_14); // Position
|
|
wrapper.passthrough(Type.VAR_INT); // Type
|
|
wrapper.write(Type.COMPOUND_TAG, handleBlockEntity(wrapper.read(Type.NAMED_COMPOUND_TAG)));
|
|
});
|
|
|
|
protocol.registerClientbound(ClientboundPackets1_19_4.CHUNK_DATA, wrapper -> {
|
|
final EntityTracker tracker = protocol.getEntityRewriter().tracker(wrapper.user());
|
|
final Type<Chunk> chunkType = new ChunkType1_18(tracker.currentWorldSectionHeight(),
|
|
MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().size()),
|
|
MathUtil.ceilLog2(tracker.biomesSent()));
|
|
final Chunk chunk = wrapper.read(chunkType);
|
|
|
|
final Type<Chunk> newChunkType = new ChunkType1_20_2(tracker.currentWorldSectionHeight(),
|
|
MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().mappedSize()),
|
|
MathUtil.ceilLog2(tracker.biomesSent()));
|
|
wrapper.write(newChunkType, chunk);
|
|
|
|
for (final ChunkSection section : chunk.getSections()) {
|
|
final DataPalette blockPalette = section.palette(PaletteType.BLOCKS);
|
|
for (int i = 0; i < blockPalette.size(); i++) {
|
|
final int id = blockPalette.idByIndex(i);
|
|
blockPalette.setIdByIndex(i, protocol.getMappingData().getNewBlockStateId(id));
|
|
}
|
|
}
|
|
|
|
for (final BlockEntity blockEntity : chunk.blockEntities()) {
|
|
handleBlockEntity(blockEntity.tag());
|
|
}
|
|
});
|
|
|
|
// Replace the NBT type everywhere
|
|
protocol.registerClientbound(ClientboundPackets1_19_4.WINDOW_ITEMS, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.UNSIGNED_BYTE); // Window id
|
|
map(Type.VAR_INT); // State id
|
|
handler(wrapper -> {
|
|
final Item[] items = wrapper.read(Type.ITEM1_13_2_ARRAY);
|
|
for (final Item item : items) {
|
|
handleItemToClient(item);
|
|
}
|
|
wrapper.write(Type.ITEM1_20_2_ARRAY, items);
|
|
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.ITEM1_13_2))); // Carried item
|
|
});
|
|
}
|
|
});
|
|
protocol.registerClientbound(ClientboundPackets1_19_4.SET_SLOT, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.UNSIGNED_BYTE); // Window id
|
|
map(Type.VAR_INT); // State id
|
|
map(Type.SHORT); // Slot id
|
|
handler(wrapper -> wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.ITEM1_13_2))));
|
|
}
|
|
});
|
|
protocol.registerClientbound(ClientboundPackets1_19_4.ADVANCEMENTS, wrapper -> {
|
|
wrapper.passthrough(Type.BOOLEAN); // Reset/clear
|
|
final int size = wrapper.passthrough(Type.VAR_INT); // Mapping size
|
|
for (int i = 0; i < size; i++) {
|
|
wrapper.passthrough(Type.STRING); // Identifier
|
|
|
|
// Parent
|
|
if (wrapper.passthrough(Type.BOOLEAN))
|
|
wrapper.passthrough(Type.STRING);
|
|
|
|
// Display data
|
|
if (wrapper.passthrough(Type.BOOLEAN)) {
|
|
wrapper.passthrough(Type.COMPONENT); // Title
|
|
wrapper.passthrough(Type.COMPONENT); // Description
|
|
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.ITEM1_13_2))); // Icon
|
|
wrapper.passthrough(Type.VAR_INT); // Frame type
|
|
final int flags = wrapper.passthrough(Type.INT); // Flags
|
|
if ((flags & 1) != 0) {
|
|
wrapper.passthrough(Type.STRING); // Background texture
|
|
}
|
|
wrapper.passthrough(Type.FLOAT); // X
|
|
wrapper.passthrough(Type.FLOAT); // Y
|
|
}
|
|
|
|
// Remove criterion triggers
|
|
wrapper.read(Type.STRING_ARRAY); // Criteria
|
|
|
|
final int requirements = wrapper.passthrough(Type.VAR_INT);
|
|
for (int array = 0; array < requirements; array++) {
|
|
wrapper.passthrough(Type.STRING_ARRAY);
|
|
}
|
|
|
|
wrapper.passthrough(Type.BOOLEAN); // Send telemetry
|
|
}
|
|
});
|
|
protocol.registerClientbound(ClientboundPackets1_19_4.ENTITY_EQUIPMENT, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.VAR_INT); // 0 - Entity ID
|
|
handler(wrapper -> {
|
|
byte slot;
|
|
do {
|
|
slot = wrapper.passthrough(Type.BYTE);
|
|
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.ITEM1_13_2)));
|
|
} while ((slot & 0xFFFFFF80) != 0);
|
|
});
|
|
}
|
|
});
|
|
protocol.registerServerbound(ServerboundPackets1_20_2.CLICK_WINDOW, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.UNSIGNED_BYTE); // Window Id
|
|
map(Type.VAR_INT); // State id
|
|
map(Type.SHORT); // Slot
|
|
map(Type.BYTE); // Button
|
|
map(Type.VAR_INT); // Mode
|
|
|
|
handler(wrapper -> {
|
|
// Affected items
|
|
final int length = wrapper.passthrough(Type.VAR_INT);
|
|
for (int i = 0; i < length; i++) {
|
|
wrapper.passthrough(Type.SHORT); // Slot
|
|
wrapper.write(Type.ITEM1_13_2, handleItemToServer(wrapper.read(Type.ITEM1_20_2)));
|
|
}
|
|
|
|
// Carried item
|
|
wrapper.write(Type.ITEM1_13_2, handleItemToServer(wrapper.read(Type.ITEM1_20_2)));
|
|
});
|
|
}
|
|
});
|
|
protocol.registerClientbound(ClientboundPackets1_19_4.TRADE_LIST, wrapper -> {
|
|
wrapper.passthrough(Type.VAR_INT); // Container id
|
|
final int size = wrapper.passthrough(Type.VAR_INT);
|
|
for (int i = 0; i < size; i++) {
|
|
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.ITEM1_13_2))); // Input
|
|
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.ITEM1_13_2))); // Output
|
|
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.ITEM1_13_2))); // Second Item
|
|
|
|
wrapper.passthrough(Type.BOOLEAN); // Trade disabled
|
|
wrapper.passthrough(Type.INT); // Number of tools uses
|
|
wrapper.passthrough(Type.INT); // Maximum number of trade uses
|
|
|
|
wrapper.passthrough(Type.INT); // XP
|
|
wrapper.passthrough(Type.INT); // Special price
|
|
wrapper.passthrough(Type.FLOAT); // Price multiplier
|
|
wrapper.passthrough(Type.INT); // Demand
|
|
}
|
|
});
|
|
protocol.registerServerbound(ServerboundPackets1_20_2.CREATIVE_INVENTORY_ACTION, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.SHORT); // 0 - Slot
|
|
handler(wrapper -> wrapper.write(Type.ITEM1_13_2, handleItemToServer(wrapper.read(Type.ITEM1_20_2)))); // 1 - Clicked Item
|
|
}
|
|
});
|
|
protocol.registerClientbound(ClientboundPackets1_19_4.SPAWN_PARTICLE, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.VAR_INT); // 0 - Particle ID
|
|
map(Type.BOOLEAN); // 1 - Long Distance
|
|
map(Type.DOUBLE); // 2 - X
|
|
map(Type.DOUBLE); // 3 - Y
|
|
map(Type.DOUBLE); // 4 - Z
|
|
map(Type.FLOAT); // 5 - Offset X
|
|
map(Type.FLOAT); // 6 - Offset Y
|
|
map(Type.FLOAT); // 7 - Offset Z
|
|
map(Type.FLOAT); // 8 - Particle Data
|
|
map(Type.INT); // 9 - Particle Count
|
|
handler(wrapper -> {
|
|
final int id = wrapper.get(Type.VAR_INT, 0);
|
|
final ParticleMappings mappings = Protocol1_20_2To1_20.MAPPINGS.getParticleMappings();
|
|
if (mappings.isBlockParticle(id)) {
|
|
final int data = wrapper.read(Type.VAR_INT);
|
|
wrapper.write(Type.VAR_INT, protocol.getMappingData().getNewBlockStateId(data));
|
|
} else if (mappings.isItemParticle(id)) {
|
|
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.ITEM1_13_2)));
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
new RecipeRewriter1_19_4<ClientboundPackets1_19_4>(protocol) {
|
|
@Override
|
|
public void handleCraftingShapeless(final PacketWrapper wrapper) throws Exception {
|
|
wrapper.passthrough(Type.STRING); // Group
|
|
wrapper.passthrough(Type.VAR_INT); // Crafting book category
|
|
handleIngredients(wrapper);
|
|
|
|
final Item result = wrapper.read(itemType());
|
|
rewrite(result);
|
|
wrapper.write(Type.ITEM1_20_2, result);
|
|
}
|
|
|
|
@Override
|
|
public void handleSmelting(final PacketWrapper wrapper) throws Exception {
|
|
wrapper.passthrough(Type.STRING); // Group
|
|
wrapper.passthrough(Type.VAR_INT); // Crafting book category
|
|
handleIngredient(wrapper);
|
|
|
|
final Item result = wrapper.read(itemType());
|
|
rewrite(result);
|
|
wrapper.write(Type.ITEM1_20_2, result);
|
|
|
|
wrapper.passthrough(Type.FLOAT); // EXP
|
|
wrapper.passthrough(Type.VAR_INT); // Cooking time
|
|
}
|
|
|
|
@Override
|
|
public void handleCraftingShaped(final PacketWrapper wrapper) throws Exception {
|
|
final int ingredients = wrapper.passthrough(Type.VAR_INT) * wrapper.passthrough(Type.VAR_INT);
|
|
wrapper.passthrough(Type.STRING); // Group
|
|
wrapper.passthrough(Type.VAR_INT); // Crafting book category
|
|
for (int i = 0; i < ingredients; i++) {
|
|
handleIngredient(wrapper);
|
|
}
|
|
|
|
final Item result = wrapper.read(itemType());
|
|
rewrite(result);
|
|
wrapper.write(Type.ITEM1_20_2, result);
|
|
|
|
wrapper.passthrough(Type.BOOLEAN); // Show notification
|
|
}
|
|
|
|
@Override
|
|
public void handleStonecutting(final PacketWrapper wrapper) throws Exception {
|
|
wrapper.passthrough(Type.STRING); // Group
|
|
handleIngredient(wrapper);
|
|
|
|
final Item result = wrapper.read(itemType());
|
|
rewrite(result);
|
|
wrapper.write(Type.ITEM1_20_2, result);
|
|
}
|
|
|
|
@Override
|
|
public void handleSmithing(final PacketWrapper wrapper) throws Exception {
|
|
handleIngredient(wrapper); // Base
|
|
handleIngredient(wrapper); // Addition
|
|
|
|
final Item result = wrapper.read(itemType());
|
|
rewrite(result);
|
|
wrapper.write(Type.ITEM1_20_2, result);
|
|
}
|
|
|
|
@Override
|
|
public void handleSmithingTransform(final PacketWrapper wrapper) throws Exception {
|
|
handleIngredient(wrapper); // Template
|
|
handleIngredient(wrapper); // Base
|
|
handleIngredient(wrapper); // Additions
|
|
|
|
final Item result = wrapper.read(itemType());
|
|
rewrite(result);
|
|
wrapper.write(Type.ITEM1_20_2, result);
|
|
}
|
|
|
|
@Override
|
|
protected void handleIngredient(final PacketWrapper wrapper) throws Exception {
|
|
final Item[] items = wrapper.read(itemArrayType());
|
|
wrapper.write(Type.ITEM1_20_2_ARRAY, items);
|
|
for (final Item item : items) {
|
|
rewrite(item);
|
|
}
|
|
}
|
|
}.register(ClientboundPackets1_19_4.DECLARE_RECIPES);
|
|
}
|
|
|
|
@Override
|
|
public @Nullable Item handleItemToClient(@Nullable final Item item) {
|
|
if (item == null) {
|
|
return null;
|
|
}
|
|
|
|
if (item.tag() != null) {
|
|
to1_20_2Effects(item);
|
|
}
|
|
|
|
return super.handleItemToClient(item);
|
|
}
|
|
|
|
@Override
|
|
public @Nullable Item handleItemToServer(@Nullable final Item item) {
|
|
if (item == null) {
|
|
return null;
|
|
}
|
|
|
|
if (item.tag() != null) {
|
|
to1_20_1Effects(item);
|
|
}
|
|
|
|
return super.handleItemToServer(item);
|
|
}
|
|
|
|
public static void to1_20_2Effects(final Item item) {
|
|
final Tag customPotionEffectsTag = item.tag().remove("CustomPotionEffects");
|
|
if (customPotionEffectsTag instanceof ListTag) {
|
|
final ListTag<?> effectsTag = (ListTag<?>) customPotionEffectsTag;
|
|
item.tag().put("custom_potion_effects", customPotionEffectsTag);
|
|
|
|
for (final Tag tag : effectsTag) {
|
|
if (!(tag instanceof CompoundTag)) {
|
|
continue;
|
|
}
|
|
|
|
final CompoundTag effectTag = (CompoundTag) tag;
|
|
final Tag idTag = effectTag.remove("Id");
|
|
if (idTag instanceof NumberTag) {
|
|
final String key = PotionEffects.idToKey(((NumberTag) idTag).asInt());
|
|
if (key != null) {
|
|
effectTag.put("id", new StringTag(key));
|
|
}
|
|
}
|
|
|
|
renameTag(effectTag, "Amplifier", "amplifier");
|
|
renameTag(effectTag, "Duration", "duration");
|
|
renameTag(effectTag, "Ambient", "ambient");
|
|
renameTag(effectTag, "ShowParticles", "show_particles");
|
|
renameTag(effectTag, "ShowIcon", "show_icon");
|
|
renameTag(effectTag, "HiddenEffect", "hidden_effect");
|
|
renameTag(effectTag, "FactorCalculationData", "factor_calculation_data");
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void to1_20_1Effects(final Item item) {
|
|
final Tag customPotionEffectsTag = item.tag().remove("custom_potion_effects");
|
|
if (customPotionEffectsTag instanceof ListTag) {
|
|
final ListTag<?> effectsTag = (ListTag<?>) customPotionEffectsTag;
|
|
item.tag().put("CustomPotionEffects", effectsTag);
|
|
|
|
for (final Tag tag : effectsTag) {
|
|
if (!(tag instanceof CompoundTag)) {
|
|
continue;
|
|
}
|
|
|
|
final CompoundTag effectTag = (CompoundTag) tag;
|
|
final Tag idTag = effectTag.remove("id");
|
|
if (idTag instanceof StringTag) {
|
|
final int id = PotionEffects.keyToId(((StringTag) idTag).getValue());
|
|
effectTag.put("Id", new IntTag(id));
|
|
}
|
|
|
|
renameTag(effectTag, "amplifier", "Amplifier");
|
|
renameTag(effectTag, "duration", "Duration");
|
|
renameTag(effectTag, "ambient", "Ambient");
|
|
renameTag(effectTag, "show_particles", "ShowParticles");
|
|
renameTag(effectTag, "show_icon", "ShowIcon");
|
|
renameTag(effectTag, "hidden_effect", "HiddenEffect");
|
|
renameTag(effectTag, "factor_calculation_data", "FactorCalculationData");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void renameTag(final CompoundTag tag, final String entryName, final String toEntryName) {
|
|
final Tag entry = tag.remove(entryName);
|
|
if (entry != null) {
|
|
tag.put(toEntryName, entry);
|
|
}
|
|
}
|
|
|
|
private @Nullable CompoundTag handleBlockEntity(@Nullable final CompoundTag tag) {
|
|
if (tag == null) {
|
|
return null;
|
|
}
|
|
|
|
final Tag primaryEffect = tag.remove("Primary");
|
|
if (primaryEffect instanceof NumberTag && ((NumberTag) primaryEffect).asInt() != 0) {
|
|
tag.put("primary_effect", new StringTag(PotionEffects.idToKeyOrLuck(((NumberTag) primaryEffect).asInt())));
|
|
}
|
|
|
|
final Tag secondaryEffect = tag.remove("Secondary");
|
|
if (secondaryEffect instanceof NumberTag && ((NumberTag) secondaryEffect).asInt() != 0) {
|
|
tag.put("secondary_effect", new StringTag(PotionEffects.idToKeyOrLuck(((NumberTag) secondaryEffect).asInt())));
|
|
}
|
|
return tag;
|
|
}
|
|
} |