1017 lines
43 KiB
Java
1017 lines
43 KiB
Java
/*
|
|
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
|
|
* Copyright (C) 2016-2021 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 nl.matsv.viabackwards.protocol.protocol1_12_2to1_13.packets;
|
|
|
|
import com.google.common.primitives.Ints;
|
|
import nl.matsv.viabackwards.ViaBackwards;
|
|
import nl.matsv.viabackwards.api.rewriters.EnchantmentRewriter;
|
|
import nl.matsv.viabackwards.protocol.protocol1_12_2to1_13.Protocol1_12_2To1_13;
|
|
import nl.matsv.viabackwards.protocol.protocol1_12_2to1_13.block_entity_handlers.FlowerPotHandler;
|
|
import nl.matsv.viabackwards.protocol.protocol1_12_2to1_13.providers.BackwardsBlockEntityProvider;
|
|
import nl.matsv.viabackwards.protocol.protocol1_12_2to1_13.storage.BackwardsBlockStorage;
|
|
import us.myles.ViaVersion.api.PacketWrapper;
|
|
import us.myles.ViaVersion.api.Via;
|
|
import us.myles.ViaVersion.api.data.UserConnection;
|
|
import us.myles.ViaVersion.api.minecraft.BlockChangeRecord;
|
|
import us.myles.ViaVersion.api.minecraft.Position;
|
|
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
|
|
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
|
import us.myles.ViaVersion.api.minecraft.item.Item;
|
|
import us.myles.ViaVersion.api.remapper.PacketHandler;
|
|
import us.myles.ViaVersion.api.remapper.PacketRemapper;
|
|
import us.myles.ViaVersion.api.rewriters.ItemRewriter;
|
|
import us.myles.ViaVersion.api.type.Type;
|
|
import us.myles.ViaVersion.protocols.protocol1_12_1to1_12.ServerboundPackets1_12_1;
|
|
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.ChatRewriter;
|
|
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.ClientboundPackets1_13;
|
|
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
|
|
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.BlockIdData;
|
|
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.SpawnEggRewriter;
|
|
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.types.Chunk1_13Type;
|
|
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.types.Chunk1_9_3_4Type;
|
|
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
|
|
import us.myles.viaversion.libs.opennbt.conversion.ConverterRegistry;
|
|
import us.myles.viaversion.libs.opennbt.tag.builtin.ByteTag;
|
|
import us.myles.viaversion.libs.opennbt.tag.builtin.CompoundTag;
|
|
import us.myles.viaversion.libs.opennbt.tag.builtin.IntTag;
|
|
import us.myles.viaversion.libs.opennbt.tag.builtin.ListTag;
|
|
import us.myles.viaversion.libs.opennbt.tag.builtin.NumberTag;
|
|
import us.myles.viaversion.libs.opennbt.tag.builtin.ShortTag;
|
|
import us.myles.viaversion.libs.opennbt.tag.builtin.StringTag;
|
|
import us.myles.viaversion.libs.opennbt.tag.builtin.Tag;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
|
|
public class BlockItemPackets1_13 extends nl.matsv.viabackwards.api.rewriters.ItemRewriter<Protocol1_12_2To1_13> {
|
|
|
|
private final Map<String, String> enchantmentMappings = new HashMap<>();
|
|
private final String extraNbtTag;
|
|
|
|
public BlockItemPackets1_13(Protocol1_12_2To1_13 protocol) {
|
|
super(protocol, null);
|
|
extraNbtTag = "VB|" + protocol.getClass().getSimpleName() + "|2";
|
|
}
|
|
|
|
public static boolean isDamageable(int id) {
|
|
return id >= 256 && id <= 259 // iron shovel, pickaxe, axe, flint and steel
|
|
|| id == 261 // bow
|
|
|| id >= 267 && id <= 279 // iron sword, wooden+stone+diamond swords, shovels, pickaxes, axes
|
|
|| id >= 283 && id <= 286 // gold sword, shovel, pickaxe, axe
|
|
|| id >= 290 && id <= 294 // hoes
|
|
|| id >= 298 && id <= 317 // armors
|
|
|| id == 346 // fishing rod
|
|
|| id == 359 // shears
|
|
|| id == 398 // carrot on a stick
|
|
|| id == 442 // shield
|
|
|| id == 443; // elytra
|
|
}
|
|
|
|
@Override
|
|
protected void registerPackets() {
|
|
protocol.registerOutgoing(ClientboundPackets1_13.COOLDOWN, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
handler(new PacketHandler() {
|
|
@Override
|
|
public void handle(PacketWrapper wrapper) throws Exception {
|
|
int itemId = wrapper.read(Type.VAR_INT);
|
|
int oldId = protocol.getMappingData().getItemMappings().get(itemId);
|
|
if (oldId != -1) {
|
|
Optional<String> eggEntityId = SpawnEggRewriter.getEntityId(oldId);
|
|
if (eggEntityId.isPresent()) {
|
|
itemId = 383 << 16;
|
|
} else {
|
|
itemId = (oldId >> 4) << 16 | oldId & 0xF;
|
|
}
|
|
}
|
|
wrapper.write(Type.VAR_INT, itemId);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
protocol.registerOutgoing(ClientboundPackets1_13.BLOCK_ACTION, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.POSITION); // Location
|
|
map(Type.UNSIGNED_BYTE); // Action Id
|
|
map(Type.UNSIGNED_BYTE); // Action param
|
|
map(Type.VAR_INT); // Block Id - /!\ NOT BLOCK STATE ID
|
|
handler(new PacketHandler() {
|
|
@Override
|
|
public void handle(PacketWrapper wrapper) throws Exception {
|
|
int blockId = wrapper.get(Type.VAR_INT, 0);
|
|
|
|
if (blockId == 73)
|
|
blockId = 25;
|
|
else if (blockId == 99)
|
|
blockId = 33;
|
|
else if (blockId == 92)
|
|
blockId = 29;
|
|
else if (blockId == 142)
|
|
blockId = 54;
|
|
else if (blockId == 305)
|
|
blockId = 146;
|
|
else if (blockId == 249)
|
|
blockId = 130;
|
|
else if (blockId == 257)
|
|
blockId = 138;
|
|
else if (blockId == 140)
|
|
blockId = 52;
|
|
else if (blockId == 472)
|
|
blockId = 209;
|
|
else if (blockId >= 483 && blockId <= 498)
|
|
blockId = blockId - 483 + 219;
|
|
|
|
wrapper.set(Type.VAR_INT, 0, blockId);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
protocol.registerOutgoing(ClientboundPackets1_13.BLOCK_ENTITY_DATA, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.POSITION); // 0 - Position
|
|
map(Type.UNSIGNED_BYTE); // 1 - Action
|
|
map(Type.NBT); // 2 - NBT Data
|
|
|
|
handler(new PacketHandler() {
|
|
@Override
|
|
public void handle(PacketWrapper wrapper) throws Exception {
|
|
BackwardsBlockEntityProvider provider = Via.getManager().getProviders().get(BackwardsBlockEntityProvider.class);
|
|
|
|
// TODO conduit handling
|
|
if (wrapper.get(Type.UNSIGNED_BYTE, 0) == 5) {
|
|
wrapper.cancel();
|
|
}
|
|
|
|
wrapper.set(Type.NBT, 0,
|
|
provider.transform(
|
|
wrapper.user(),
|
|
wrapper.get(Type.POSITION, 0),
|
|
wrapper.get(Type.NBT, 0)
|
|
));
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
protocol.registerOutgoing(ClientboundPackets1_13.UNLOAD_CHUNK, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
handler(new PacketHandler() {
|
|
@Override
|
|
public void handle(PacketWrapper wrapper) throws Exception {
|
|
int chunkMinX = wrapper.passthrough(Type.INT) << 4;
|
|
int chunkMinZ = wrapper.passthrough(Type.INT) << 4;
|
|
int chunkMaxX = chunkMinX + 15;
|
|
int chunkMaxZ = chunkMinZ + 15;
|
|
BackwardsBlockStorage blockStorage = wrapper.user().get(BackwardsBlockStorage.class);
|
|
blockStorage.getBlocks().entrySet().removeIf(entry -> {
|
|
Position position = entry.getKey();
|
|
return position.getX() >= chunkMinX && position.getZ() >= chunkMinZ
|
|
&& position.getX() <= chunkMaxX && position.getZ() <= chunkMaxZ;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Block Change
|
|
protocol.registerOutgoing(ClientboundPackets1_13.BLOCK_CHANGE, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.POSITION); // 0 - Position
|
|
|
|
handler(new PacketHandler() {
|
|
@Override
|
|
public void handle(PacketWrapper wrapper) throws Exception {
|
|
int blockState = wrapper.read(Type.VAR_INT);
|
|
Position position = wrapper.get(Type.POSITION, 0);
|
|
|
|
// Store blocks
|
|
BackwardsBlockStorage storage = wrapper.user().get(BackwardsBlockStorage.class);
|
|
storage.checkAndStore(position, blockState);
|
|
|
|
wrapper.write(Type.VAR_INT, protocol.getMappingData().getNewBlockStateId(blockState));
|
|
|
|
// Flower pot special treatment
|
|
flowerPotSpecialTreatment(wrapper.user(), blockState, position);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Multi Block Change
|
|
protocol.registerOutgoing(ClientboundPackets1_13.MULTI_BLOCK_CHANGE, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.INT); // 0 - Chunk X
|
|
map(Type.INT); // 1 - Chunk Z
|
|
map(Type.BLOCK_CHANGE_RECORD_ARRAY);
|
|
handler(new PacketHandler() {
|
|
@Override
|
|
public void handle(PacketWrapper wrapper) throws Exception {
|
|
BackwardsBlockStorage storage = wrapper.user().get(BackwardsBlockStorage.class);
|
|
|
|
for (BlockChangeRecord record : wrapper.get(Type.BLOCK_CHANGE_RECORD_ARRAY, 0)) {
|
|
int chunkX = wrapper.get(Type.INT, 0);
|
|
int chunkZ = wrapper.get(Type.INT, 1);
|
|
int block = record.getBlockId();
|
|
Position position = new Position(
|
|
record.getSectionX() + (chunkX * 16),
|
|
record.getY(),
|
|
record.getSectionZ() + (chunkZ * 16));
|
|
|
|
// Store if needed
|
|
storage.checkAndStore(position, block);
|
|
|
|
// Flower pot special treatment
|
|
flowerPotSpecialTreatment(wrapper.user(), block, position);
|
|
|
|
// Change to old id
|
|
record.setBlockId(protocol.getMappingData().getNewBlockStateId(block));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
ItemRewriter itemRewriter = new ItemRewriter(protocol, this::handleItemToClient, this::handleItemToServer);
|
|
|
|
protocol.registerOutgoing(ClientboundPackets1_13.WINDOW_ITEMS, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.UNSIGNED_BYTE);
|
|
map(Type.FLAT_ITEM_ARRAY, Type.ITEM_ARRAY);
|
|
|
|
handler(itemRewriter.itemArrayHandler(Type.ITEM_ARRAY));
|
|
}
|
|
});
|
|
|
|
protocol.registerOutgoing(ClientboundPackets1_13.SET_SLOT, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.BYTE);
|
|
map(Type.SHORT);
|
|
map(Type.FLAT_ITEM, Type.ITEM);
|
|
|
|
handler(itemRewriter.itemToClientHandler(Type.ITEM));
|
|
}
|
|
});
|
|
|
|
protocol.registerOutgoing(ClientboundPackets1_13.CHUNK_DATA, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
handler(wrapper -> {
|
|
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
|
|
|
|
Chunk1_9_3_4Type type_old = new Chunk1_9_3_4Type(clientWorld);
|
|
Chunk1_13Type type = new Chunk1_13Type(clientWorld);
|
|
Chunk chunk = wrapper.read(type);
|
|
|
|
// Handle Block Entities before block rewrite
|
|
BackwardsBlockEntityProvider provider = Via.getManager().getProviders().get(BackwardsBlockEntityProvider.class);
|
|
BackwardsBlockStorage storage = wrapper.user().get(BackwardsBlockStorage.class);
|
|
for (CompoundTag tag : chunk.getBlockEntities()) {
|
|
Tag idTag = tag.get("id");
|
|
if (idTag == null) continue;
|
|
|
|
String id = (String) idTag.getValue();
|
|
|
|
// Ignore if we don't handle it
|
|
if (!provider.isHandled(id)) continue;
|
|
|
|
int sectionIndex = ((NumberTag) tag.get("y")).asInt() >> 4;
|
|
ChunkSection section = chunk.getSections()[sectionIndex];
|
|
|
|
int x = ((NumberTag) tag.get("x")).asInt();
|
|
int y = ((NumberTag) tag.get("y")).asInt();
|
|
int z = ((NumberTag) tag.get("z")).asInt();
|
|
Position position = new Position(x, (short) y, z);
|
|
|
|
int block = section.getFlatBlock(x & 0xF, y & 0xF, z & 0xF);
|
|
storage.checkAndStore(position, block);
|
|
|
|
provider.transform(wrapper.user(), position, tag);
|
|
}
|
|
|
|
// Rewrite new blocks to old blocks
|
|
for (int i = 0; i < chunk.getSections().length; i++) {
|
|
ChunkSection section = chunk.getSections()[i];
|
|
if (section == null) {
|
|
continue;
|
|
}
|
|
|
|
// Flower pots require a special treatment, they are no longer block entities :(
|
|
for (int y = 0; y < 16; y++) {
|
|
for (int z = 0; z < 16; z++) {
|
|
for (int x = 0; x < 16; x++) {
|
|
int block = section.getFlatBlock(x, y, z);
|
|
|
|
// Check if the block is a flower
|
|
if (FlowerPotHandler.isFlowah(block)) {
|
|
Position pos = new Position(
|
|
(x + (chunk.getX() << 4)),
|
|
(short) (y + (i << 4)),
|
|
(z + (chunk.getZ() << 4))
|
|
);
|
|
// Store block
|
|
storage.checkAndStore(pos, block);
|
|
|
|
CompoundTag nbt = provider.transform(wrapper.user(), pos, "minecraft:flower_pot");
|
|
|
|
chunk.getBlockEntities().add(nbt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int p = 0; p < section.getPaletteSize(); p++) {
|
|
int old = section.getPaletteEntry(p);
|
|
if (old != 0) {
|
|
int oldId = protocol.getMappingData().getNewBlockStateId(old);
|
|
section.setPaletteEntry(p, oldId);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (chunk.isBiomeData()) {
|
|
for (int i = 0; i < 256; i++) {
|
|
int biome = chunk.getBiomeData()[i];
|
|
int newId = -1;
|
|
switch (biome) {
|
|
case 40: // end biomes
|
|
case 41:
|
|
case 42:
|
|
case 43:
|
|
newId = 9;
|
|
break;
|
|
case 47: // deep ocean biomes
|
|
case 48:
|
|
case 49:
|
|
newId = 24;
|
|
break;
|
|
case 50: // deep frozen... let's just pick the frozen variant
|
|
newId = 10;
|
|
break;
|
|
case 44: // the other new ocean biomes
|
|
case 45:
|
|
case 46:
|
|
newId = 0;
|
|
break;
|
|
}
|
|
|
|
if (newId != -1) {
|
|
chunk.getBiomeData()[i] = newId;
|
|
}
|
|
}
|
|
}
|
|
|
|
wrapper.write(type_old, chunk);
|
|
});
|
|
}
|
|
});
|
|
|
|
protocol.registerOutgoing(ClientboundPackets1_13.EFFECT, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.INT); // Effect Id
|
|
map(Type.POSITION); // Location
|
|
map(Type.INT); // Data
|
|
handler(new PacketHandler() {
|
|
@Override
|
|
public void handle(PacketWrapper wrapper) throws Exception {
|
|
int id = wrapper.get(Type.INT, 0);
|
|
int data = wrapper.get(Type.INT, 1);
|
|
if (id == 1010) { // Play record
|
|
wrapper.set(Type.INT, 1, protocol.getMappingData().getItemMappings().get(data) >> 4);
|
|
} else if (id == 2001) { // Block break + block break sound
|
|
data = protocol.getMappingData().getNewBlockStateId(data);
|
|
int blockId = data >> 4;
|
|
int blockData = data & 0xF;
|
|
wrapper.set(Type.INT, 1, (blockId & 0xFFF) | (blockData << 12));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
protocol.registerOutgoing(ClientboundPackets1_13.MAP_DATA, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.VAR_INT);
|
|
map(Type.BYTE);
|
|
map(Type.BOOLEAN);
|
|
handler(new PacketHandler() {
|
|
@Override
|
|
public void handle(PacketWrapper wrapper) throws Exception {
|
|
int iconCount = wrapper.passthrough(Type.VAR_INT);
|
|
for (int i = 0; i < iconCount; i++) {
|
|
int type = wrapper.read(Type.VAR_INT);
|
|
byte x = wrapper.read(Type.BYTE);
|
|
byte z = wrapper.read(Type.BYTE);
|
|
byte direction = wrapper.read(Type.BYTE);
|
|
if (wrapper.read(Type.BOOLEAN)) {
|
|
wrapper.read(Type.COMPONENT);
|
|
}
|
|
if (type > 9) {
|
|
wrapper.set(Type.VAR_INT, 1, wrapper.get(Type.VAR_INT, 1) - 1);
|
|
continue;
|
|
}
|
|
wrapper.write(Type.BYTE, (byte) ((type << 4) | (direction & 0x0F)));
|
|
wrapper.write(Type.BYTE, x);
|
|
wrapper.write(Type.BYTE, z);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
protocol.registerOutgoing(ClientboundPackets1_13.ENTITY_EQUIPMENT, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.VAR_INT);
|
|
map(Type.VAR_INT);
|
|
map(Type.FLAT_ITEM, Type.ITEM);
|
|
|
|
handler(itemRewriter.itemToClientHandler(Type.ITEM));
|
|
}
|
|
});
|
|
|
|
protocol.registerOutgoing(ClientboundPackets1_13.WINDOW_PROPERTY, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.UNSIGNED_BYTE); // Window Id
|
|
map(Type.SHORT); // Property
|
|
map(Type.SHORT); // Value
|
|
handler(wrapper -> {
|
|
short property = wrapper.get(Type.SHORT, 0);
|
|
// Enchantment table
|
|
if (property >= 4 && property <= 6) {
|
|
short oldId = wrapper.get(Type.SHORT, 1);
|
|
wrapper.set(Type.SHORT, 1, (short) protocol.getMappingData().getEnchantmentMappings().getNewId(oldId));
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
|
|
protocol.registerIncoming(ServerboundPackets1_12_1.CREATIVE_INVENTORY_ACTION, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.SHORT);
|
|
map(Type.ITEM, Type.FLAT_ITEM);
|
|
|
|
handler(itemRewriter.itemToServerHandler(Type.FLAT_ITEM));
|
|
}
|
|
});
|
|
|
|
protocol.registerIncoming(ServerboundPackets1_12_1.CLICK_WINDOW, new PacketRemapper() {
|
|
@Override
|
|
public void registerMap() {
|
|
map(Type.UNSIGNED_BYTE);
|
|
map(Type.SHORT);
|
|
map(Type.BYTE);
|
|
map(Type.SHORT);
|
|
map(Type.VAR_INT);
|
|
map(Type.ITEM, Type.FLAT_ITEM);
|
|
|
|
handler(itemRewriter.itemToServerHandler(Type.FLAT_ITEM));
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
protected void registerRewrites() {
|
|
enchantmentMappings.put("minecraft:loyalty", "§7Loyalty");
|
|
enchantmentMappings.put("minecraft:impaling", "§7Impaling");
|
|
enchantmentMappings.put("minecraft:riptide", "§7Riptide");
|
|
enchantmentMappings.put("minecraft:channeling", "§7Channeling");
|
|
}
|
|
|
|
@Override
|
|
public Item handleItemToClient(Item item) {
|
|
if (item == null) return null;
|
|
|
|
// Custom mappings/super call moved down
|
|
int originalId = item.getIdentifier();
|
|
|
|
Integer rawId = null;
|
|
boolean gotRawIdFromTag = false;
|
|
|
|
CompoundTag tag = item.getTag();
|
|
|
|
// Use tag to get original ID and data
|
|
Tag originalIdTag;
|
|
if (tag != null && (originalIdTag = tag.remove(extraNbtTag)) != null) {
|
|
rawId = ((NumberTag) originalIdTag).asInt();
|
|
gotRawIdFromTag = true;
|
|
}
|
|
|
|
if (rawId == null) {
|
|
// Look for custom mappings
|
|
super.handleItemToClient(item);
|
|
|
|
// Handle one-way special case
|
|
if (item.getIdentifier() == -1) {
|
|
if (originalId == 362) { // base/colorless shulker box
|
|
rawId = 0xe50000; // purple shulker box
|
|
} else {
|
|
if (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug()) {
|
|
ViaBackwards.getPlatform().getLogger().warning("Failed to get 1.12 item for " + originalId);
|
|
}
|
|
|
|
rawId = 0x10000;
|
|
}
|
|
} else { // Use the found custom mapping
|
|
// Take the newly added tag
|
|
if (tag == null) {
|
|
tag = item.getTag();
|
|
}
|
|
|
|
rawId = itemIdToRaw(item.getIdentifier(), item, tag);
|
|
}
|
|
}
|
|
|
|
item.setIdentifier(rawId >> 16);
|
|
item.setData((short) (rawId & 0xFFFF));
|
|
|
|
// NBT changes
|
|
if (tag != null) {
|
|
if (isDamageable(item.getIdentifier())) {
|
|
Tag damageTag = tag.remove("Damage");
|
|
if (!gotRawIdFromTag && damageTag instanceof IntTag) {
|
|
item.setData((short) (int) damageTag.getValue());
|
|
}
|
|
}
|
|
|
|
if (item.getIdentifier() == 358) { // map
|
|
Tag mapTag = tag.remove("map");
|
|
if (!gotRawIdFromTag && mapTag instanceof IntTag) {
|
|
item.setData((short) (int) mapTag.getValue());
|
|
}
|
|
}
|
|
|
|
// Shield and banner
|
|
invertShieldAndBannerId(item, tag);
|
|
|
|
// Display Name now uses JSON
|
|
CompoundTag display = tag.get("display");
|
|
if (display != null) {
|
|
StringTag name = display.get("Name");
|
|
if (name != null) {
|
|
display.put(extraNbtTag + "|Name", new StringTag(name.getValue()));
|
|
name.setValue(ChatRewriter.jsonToLegacyText(name.getValue()));
|
|
}
|
|
}
|
|
|
|
// ench is now Enchantments and now uses identifiers
|
|
rewriteEnchantmentsToClient(tag, false);
|
|
rewriteEnchantmentsToClient(tag, true);
|
|
|
|
rewriteCanPlaceToClient(tag, "CanPlaceOn");
|
|
rewriteCanPlaceToClient(tag, "CanDestroy");
|
|
}
|
|
return item;
|
|
}
|
|
|
|
private int itemIdToRaw(int oldId, Item item, CompoundTag tag) {
|
|
Optional<String> eggEntityId = SpawnEggRewriter.getEntityId(oldId);
|
|
if (eggEntityId.isPresent()) {
|
|
if (tag == null) {
|
|
item.setTag(tag = new CompoundTag());
|
|
}
|
|
if (!tag.contains("EntityTag")) {
|
|
CompoundTag entityTag = new CompoundTag();
|
|
entityTag.put("id", new StringTag(eggEntityId.get()));
|
|
tag.put("EntityTag", entityTag);
|
|
}
|
|
return 0x17f0000; // 383 << 16;
|
|
}
|
|
|
|
return (oldId >> 4) << 16 | oldId & 0xF;
|
|
}
|
|
|
|
private void rewriteCanPlaceToClient(CompoundTag tag, String tagName) {
|
|
// The tag was manually created incorrectly so ignore rewriting it
|
|
if (!(tag.get(tagName) instanceof ListTag)) return;
|
|
ListTag blockTag = tag.get(tagName);
|
|
if (blockTag == null) return;
|
|
|
|
ListTag newCanPlaceOn = new ListTag(StringTag.class);
|
|
tag.put(extraNbtTag + "|" + tagName, ConverterRegistry.convertToTag(ConverterRegistry.convertToValue(blockTag)));
|
|
for (Tag oldTag : blockTag) {
|
|
Object value = oldTag.getValue();
|
|
String[] newValues = value instanceof String ?
|
|
BlockIdData.fallbackReverseMapping.get(((String) value).replace("minecraft:", "")) : null;
|
|
if (newValues != null) {
|
|
for (String newValue : newValues) {
|
|
newCanPlaceOn.add(new StringTag(newValue));
|
|
}
|
|
} else {
|
|
newCanPlaceOn.add(oldTag);
|
|
}
|
|
}
|
|
tag.put(tagName, newCanPlaceOn);
|
|
}
|
|
|
|
//TODO un-ugly all of this
|
|
private void rewriteEnchantmentsToClient(CompoundTag tag, boolean storedEnch) {
|
|
String key = storedEnch ? "StoredEnchantments" : "Enchantments";
|
|
ListTag enchantments = tag.get(key);
|
|
if (enchantments == null) return;
|
|
|
|
ListTag noMapped = new ListTag(CompoundTag.class);
|
|
ListTag newEnchantments = new ListTag(CompoundTag.class);
|
|
List<Tag> lore = new ArrayList<>();
|
|
boolean hasValidEnchants = false;
|
|
for (Tag enchantmentEntryTag : enchantments.clone()) {
|
|
CompoundTag enchantmentEntry = (CompoundTag) enchantmentEntryTag;
|
|
Tag idTag = enchantmentEntry.get("id");
|
|
if (!(idTag instanceof StringTag)) continue;
|
|
|
|
String newId = (String) idTag.getValue();
|
|
int levelValue = ((NumberTag) enchantmentEntry.get("lvl")).asInt();
|
|
short level = levelValue < Short.MAX_VALUE ? (short) levelValue : Short.MAX_VALUE;
|
|
|
|
String mappedEnchantmentId = enchantmentMappings.get(newId);
|
|
if (mappedEnchantmentId != null) {
|
|
lore.add(new StringTag(mappedEnchantmentId + " " + EnchantmentRewriter.getRomanNumber(level)));
|
|
noMapped.add(enchantmentEntry);
|
|
} else if (!newId.isEmpty()) {
|
|
Short oldId = Protocol1_13To1_12_2.MAPPINGS.getOldEnchantmentsIds().inverse().get(newId);
|
|
if (oldId == null) {
|
|
if (!newId.startsWith("viaversion:legacy/")) {
|
|
// Custom enchant (?)
|
|
noMapped.add(enchantmentEntry);
|
|
|
|
// Some custom-enchant plugins write it into the lore manually, which would double its entry
|
|
if (ViaBackwards.getConfig().addCustomEnchantsToLore()) {
|
|
String name = newId;
|
|
int index = name.indexOf(':') + 1;
|
|
if (index != 0 && index != name.length()) {
|
|
name = name.substring(index);
|
|
}
|
|
name = "§7" + Character.toUpperCase(name.charAt(0)) + name.substring(1).toLowerCase(Locale.ENGLISH);
|
|
|
|
lore.add(new StringTag(name + " " + EnchantmentRewriter.getRomanNumber(level)));
|
|
}
|
|
|
|
if (Via.getManager().isDebug()) {
|
|
ViaBackwards.getPlatform().getLogger().warning("Found unknown enchant: " + newId);
|
|
}
|
|
continue;
|
|
} else {
|
|
oldId = Short.valueOf(newId.substring(18));
|
|
}
|
|
}
|
|
|
|
if (level != 0) {
|
|
hasValidEnchants = true;
|
|
}
|
|
|
|
CompoundTag newEntry = new CompoundTag();
|
|
newEntry.put("id", new ShortTag(oldId));
|
|
newEntry.put("lvl", new ShortTag(level));
|
|
newEnchantments.add(newEntry);
|
|
}
|
|
}
|
|
|
|
// Put here to hide empty enchantment from 1.14 rewrites
|
|
if (!storedEnch && !hasValidEnchants) {
|
|
IntTag hideFlags = tag.get("HideFlags");
|
|
if (hideFlags == null) {
|
|
hideFlags = new IntTag();
|
|
tag.put(extraNbtTag + "|DummyEnchant", new ByteTag());
|
|
} else {
|
|
tag.put(extraNbtTag + "|OldHideFlags", new IntTag(hideFlags.asByte()));
|
|
}
|
|
|
|
if (newEnchantments.size() == 0) {
|
|
CompoundTag enchEntry = new CompoundTag();
|
|
enchEntry.put("id", new ShortTag((short) 0));
|
|
enchEntry.put("lvl", new ShortTag((short) 0));
|
|
newEnchantments.add(enchEntry);
|
|
}
|
|
|
|
int value = hideFlags.asByte() | 1;
|
|
hideFlags.setValue(value);
|
|
tag.put("HideFlags", hideFlags);
|
|
}
|
|
|
|
if (noMapped.size() != 0) {
|
|
tag.put(extraNbtTag + "|" + key, noMapped);
|
|
|
|
if (!lore.isEmpty()) {
|
|
CompoundTag display = tag.get("display");
|
|
if (display == null) {
|
|
tag.put("display", display = new CompoundTag());
|
|
}
|
|
|
|
ListTag loreTag = display.get("Lore");
|
|
if (loreTag == null) {
|
|
display.put("Lore", loreTag = new ListTag(StringTag.class));
|
|
tag.put(extraNbtTag + "|DummyLore", new ByteTag());
|
|
} else if (loreTag.size() != 0) {
|
|
ListTag oldLore = new ListTag(StringTag.class);
|
|
for (Tag value : loreTag) {
|
|
oldLore.add(value.clone());
|
|
}
|
|
tag.put(extraNbtTag + "|OldLore", oldLore);
|
|
|
|
lore.addAll(loreTag.getValue());
|
|
}
|
|
|
|
loreTag.setValue(lore);
|
|
}
|
|
}
|
|
|
|
tag.remove("Enchantments");
|
|
tag.put(storedEnch ? key : "ench", newEnchantments);
|
|
}
|
|
|
|
@Override
|
|
public Item handleItemToServer(Item item) {
|
|
if (item == null) return null;
|
|
CompoundTag tag = item.getTag();
|
|
|
|
// Save original id
|
|
int originalId = (item.getIdentifier() << 16 | item.getData() & 0xFFFF);
|
|
|
|
int rawId = (item.getIdentifier() << 4 | item.getData() & 0xF);
|
|
|
|
// NBT Additions
|
|
if (isDamageable(item.getIdentifier())) {
|
|
if (tag == null) item.setTag(tag = new CompoundTag());
|
|
tag.put("Damage", new IntTag(item.getData()));
|
|
}
|
|
if (item.getIdentifier() == 358) { // map
|
|
if (tag == null) item.setTag(tag = new CompoundTag());
|
|
tag.put("map", new IntTag(item.getData()));
|
|
}
|
|
|
|
// NBT Changes
|
|
if (tag != null) {
|
|
// Shield and banner
|
|
invertShieldAndBannerId(item, tag);
|
|
|
|
// Display Name now uses JSON
|
|
Tag display = tag.get("display");
|
|
if (display instanceof CompoundTag) {
|
|
CompoundTag displayTag = (CompoundTag) display;
|
|
StringTag name = displayTag.get("Name");
|
|
if (name != null) {
|
|
StringTag via = displayTag.remove(extraNbtTag + "|Name");
|
|
name.setValue(via != null ? via.getValue() : ChatRewriter.legacyTextToJsonString(name.getValue()));
|
|
}
|
|
}
|
|
|
|
// ench is now Enchantments and now uses identifiers
|
|
rewriteEnchantmentsToServer(tag, false);
|
|
rewriteEnchantmentsToServer(tag, true);
|
|
|
|
rewriteCanPlaceToServer(tag, "CanPlaceOn");
|
|
rewriteCanPlaceToServer(tag, "CanDestroy");
|
|
|
|
// Handle SpawnEggs
|
|
if (item.getIdentifier() == 383) {
|
|
CompoundTag entityTag = tag.get("EntityTag");
|
|
StringTag identifier;
|
|
if (entityTag != null && (identifier = entityTag.get("id")) != null) {
|
|
rawId = SpawnEggRewriter.getSpawnEggId(identifier.getValue());
|
|
if (rawId == -1) {
|
|
rawId = 25100288; // Bat fallback
|
|
} else {
|
|
entityTag.remove("id");
|
|
if (entityTag.isEmpty()) {
|
|
tag.remove("EntityTag");
|
|
}
|
|
}
|
|
} else {
|
|
// Fallback to bat
|
|
rawId = 25100288;
|
|
}
|
|
}
|
|
if (tag.isEmpty()) {
|
|
item.setTag(tag = null);
|
|
}
|
|
}
|
|
|
|
// Handle custom mappings
|
|
int identifier = item.getIdentifier();
|
|
item.setIdentifier(rawId);
|
|
super.handleItemToServer(item);
|
|
|
|
// Mapped with original data, we can return here
|
|
if (item.getIdentifier() != rawId && item.getIdentifier() != -1) return item;
|
|
|
|
// Set to legacy id again
|
|
item.setIdentifier(identifier);
|
|
|
|
int newId = -1;
|
|
if (!protocol.getMappingData().getItemMappings().inverse().containsKey(rawId)) {
|
|
if (!isDamageable(item.getIdentifier()) && item.getIdentifier() != 358) { // Map
|
|
if (tag == null) item.setTag(tag = new CompoundTag());
|
|
tag.put(extraNbtTag, new IntTag(originalId)); // Data will be lost, saving original id
|
|
}
|
|
|
|
if (item.getIdentifier() == 229) { // purple shulker box
|
|
newId = 362; // directly set the new id -> base/colorless shulker box
|
|
} else if (item.getIdentifier() == 31 && item.getData() == 0) { // Shrub was removed
|
|
rawId = 32 << 4; // Dead Bush
|
|
} else if (protocol.getMappingData().getItemMappings().inverse().containsKey(rawId & ~0xF)) {
|
|
rawId &= ~0xF; // Remove data
|
|
} else {
|
|
if (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug()) {
|
|
ViaBackwards.getPlatform().getLogger().warning("Failed to get 1.13 item for " + item.getIdentifier());
|
|
}
|
|
rawId = 16; // Stone
|
|
}
|
|
}
|
|
|
|
if (newId == -1) {
|
|
newId = protocol.getMappingData().getItemMappings().inverse().get(rawId);
|
|
}
|
|
|
|
item.setIdentifier(newId);
|
|
item.setData((short) 0);
|
|
return item;
|
|
}
|
|
|
|
private void rewriteCanPlaceToServer(CompoundTag tag, String tagName) {
|
|
if (!(tag.get(tagName) instanceof ListTag)) return;
|
|
ListTag blockTag = tag.remove(extraNbtTag + "|" + tagName);
|
|
if (blockTag != null) {
|
|
tag.put(tagName, ConverterRegistry.convertToTag(ConverterRegistry.convertToValue(blockTag)));
|
|
} else if ((blockTag = tag.get(tagName)) != null) {
|
|
ListTag newCanPlaceOn = new ListTag(StringTag.class);
|
|
for (Tag oldTag : blockTag) {
|
|
Object value = oldTag.getValue();
|
|
String oldId = value.toString().replace("minecraft:", "");
|
|
int key = Ints.tryParse(oldId);
|
|
String numberConverted = BlockIdData.numberIdToString.get(key);
|
|
if (numberConverted != null) {
|
|
oldId = numberConverted;
|
|
}
|
|
|
|
String lowerCaseId = oldId.toLowerCase(Locale.ROOT);
|
|
String[] newValues = BlockIdData.blockIdMapping.get(lowerCaseId);
|
|
if (newValues != null) {
|
|
for (String newValue : newValues) {
|
|
newCanPlaceOn.add(new StringTag(newValue));
|
|
}
|
|
} else {
|
|
newCanPlaceOn.add(new StringTag(lowerCaseId));
|
|
}
|
|
}
|
|
tag.put(tagName, newCanPlaceOn);
|
|
}
|
|
}
|
|
|
|
private void rewriteEnchantmentsToServer(CompoundTag tag, boolean storedEnch) {
|
|
String key = storedEnch ? "StoredEnchantments" : "Enchantments";
|
|
ListTag enchantments = tag.get(storedEnch ? key : "ench");
|
|
if (enchantments == null) return;
|
|
|
|
ListTag newEnchantments = new ListTag(CompoundTag.class);
|
|
boolean dummyEnchant = false;
|
|
if (!storedEnch) {
|
|
IntTag hideFlags = tag.remove(extraNbtTag + "|OldHideFlags");
|
|
if (hideFlags != null) {
|
|
tag.put("HideFlags", new IntTag(hideFlags.asByte()));
|
|
dummyEnchant = true;
|
|
} else if (tag.remove(extraNbtTag + "|DummyEnchant") != null) {
|
|
tag.remove("HideFlags");
|
|
dummyEnchant = true;
|
|
}
|
|
}
|
|
|
|
for (Tag enchEntry : enchantments) {
|
|
CompoundTag enchantmentEntry = new CompoundTag();
|
|
short oldId = ((NumberTag) ((CompoundTag) enchEntry).get("id")).asShort();
|
|
short level = ((NumberTag) ((CompoundTag) enchEntry).get("lvl")).asShort();
|
|
if (dummyEnchant && oldId == 0 && level == 0) {
|
|
continue; //Skip dummy enchatment
|
|
}
|
|
String newId = Protocol1_13To1_12_2.MAPPINGS.getOldEnchantmentsIds().get(oldId);
|
|
if (newId == null) {
|
|
newId = "viaversion:legacy/" + oldId;
|
|
}
|
|
enchantmentEntry.put("id", new StringTag(newId));
|
|
|
|
enchantmentEntry.put("lvl", new ShortTag(level));
|
|
newEnchantments.add(enchantmentEntry);
|
|
}
|
|
|
|
ListTag noMapped = tag.remove(extraNbtTag + "|Enchantments");
|
|
if (noMapped != null) {
|
|
for (Tag value : noMapped) {
|
|
newEnchantments.add(value);
|
|
}
|
|
}
|
|
|
|
CompoundTag display = tag.get("display");
|
|
if (display == null) {
|
|
tag.put("display", display = new CompoundTag());
|
|
}
|
|
|
|
ListTag oldLore = tag.remove(extraNbtTag + "|OldLore");
|
|
if (oldLore != null) {
|
|
ListTag lore = display.get("Lore");
|
|
if (lore == null) {
|
|
tag.put("Lore", lore = new ListTag());
|
|
}
|
|
|
|
lore.setValue(oldLore.getValue());
|
|
} else if (tag.remove(extraNbtTag + "|DummyLore") != null) {
|
|
display.remove("Lore");
|
|
if (display.isEmpty()) {
|
|
tag.remove("display");
|
|
}
|
|
}
|
|
|
|
if (!storedEnch) {
|
|
tag.remove("ench");
|
|
}
|
|
tag.put(key, newEnchantments);
|
|
}
|
|
|
|
private void invertShieldAndBannerId(Item item, CompoundTag tag) {
|
|
if (item.getIdentifier() != 442 && item.getIdentifier() != 425) return;
|
|
|
|
Tag blockEntityTag = tag.get("BlockEntityTag");
|
|
if (!(blockEntityTag instanceof CompoundTag)) return;
|
|
|
|
CompoundTag blockEntityCompoundTag = (CompoundTag) blockEntityTag;
|
|
Tag base = blockEntityCompoundTag.get("Base");
|
|
if (base instanceof IntTag) {
|
|
IntTag baseTag = (IntTag) base;
|
|
baseTag.setValue(15 - baseTag.asInt()); // invert color id
|
|
}
|
|
|
|
Tag patterns = blockEntityCompoundTag.get("Patterns");
|
|
if (patterns instanceof ListTag) {
|
|
ListTag patternsTag = (ListTag) patterns;
|
|
for (Tag pattern : patternsTag) {
|
|
if (!(pattern instanceof CompoundTag)) continue;
|
|
|
|
IntTag colorTag = ((CompoundTag) pattern).get("Color");
|
|
colorTag.setValue(15 - colorTag.asInt()); // Invert color id
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO find a less hacky way to do this (https://bugs.mojang.com/browse/MC-74231)
|
|
private static void flowerPotSpecialTreatment(UserConnection user, int blockState, Position position) throws Exception {
|
|
if (FlowerPotHandler.isFlowah(blockState)) {
|
|
BackwardsBlockEntityProvider beProvider = Via.getManager().getProviders().get(BackwardsBlockEntityProvider.class);
|
|
|
|
CompoundTag nbt = beProvider.transform(user, position, "minecraft:flower_pot");
|
|
|
|
// Remove the flowerpot
|
|
PacketWrapper blockUpdateRemove = new PacketWrapper(0x0B, null, user);
|
|
blockUpdateRemove.write(Type.POSITION, position);
|
|
blockUpdateRemove.write(Type.VAR_INT, 0);
|
|
blockUpdateRemove.send(Protocol1_12_2To1_13.class, true);
|
|
|
|
// Create the flowerpot
|
|
PacketWrapper blockCreate = new PacketWrapper(0x0B, null, user);
|
|
blockCreate.write(Type.POSITION, position);
|
|
blockCreate.write(Type.VAR_INT, Protocol1_12_2To1_13.MAPPINGS.getNewBlockStateId(blockState));
|
|
blockCreate.send(Protocol1_12_2To1_13.class, true);
|
|
|
|
// Send a block entity update
|
|
PacketWrapper wrapper = new PacketWrapper(0x09, null, user);
|
|
wrapper.write(Type.POSITION, position);
|
|
wrapper.write(Type.UNSIGNED_BYTE, (short) 5);
|
|
wrapper.write(Type.NBT, nbt);
|
|
wrapper.send(Protocol1_12_2To1_13.class, true);
|
|
|
|
}
|
|
}
|
|
}
|