/* * 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 . */ package nl.matsv.viabackwards.api.rewriters; import nl.matsv.viabackwards.api.BackwardsProtocol; import nl.matsv.viabackwards.api.data.MappedLegacyBlockItem; import nl.matsv.viabackwards.api.data.VBMappingDataLoader; import nl.matsv.viabackwards.protocol.protocol1_11_1to1_12.data.BlockColors; import nl.matsv.viabackwards.utils.Block; import org.jetbrains.annotations.Nullable; 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.protocols.protocol1_13to1_12_2.ChatRewriter; import us.myles.viaversion.libs.fastutil.ints.Int2ObjectMap; import us.myles.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap; import us.myles.viaversion.libs.gson.JsonElement; import us.myles.viaversion.libs.gson.JsonObject; import us.myles.viaversion.libs.gson.JsonPrimitive; 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.NumberTag; import us.myles.viaversion.libs.opennbt.tag.builtin.StringTag; import us.myles.viaversion.libs.opennbt.tag.builtin.Tag; import java.util.HashMap; import java.util.Map; public abstract class LegacyBlockItemRewriter extends ItemRewriterBase { private static final Map> LEGACY_MAPPINGS = new HashMap<>(); protected final Int2ObjectMap replacementData; static { JsonObject jsonObject = VBMappingDataLoader.loadFromDataDir("legacy-mappings.json"); for (Map.Entry entry : jsonObject.entrySet()) { Int2ObjectMap mappings = new Int2ObjectOpenHashMap<>(8); LEGACY_MAPPINGS.put(entry.getKey(), mappings); for (Map.Entry dataEntry : entry.getValue().getAsJsonObject().entrySet()) { JsonObject object = dataEntry.getValue().getAsJsonObject(); int id = object.getAsJsonPrimitive("id").getAsInt(); JsonPrimitive jsonData = object.getAsJsonPrimitive("data"); short data = jsonData != null ? jsonData.getAsShort() : 0; String name = object.getAsJsonPrimitive("name").getAsString(); JsonPrimitive blockField = object.getAsJsonPrimitive("block"); boolean block = blockField != null && blockField.getAsBoolean(); if (dataEntry.getKey().indexOf('-') != -1) { // Range of ids String[] split = dataEntry.getKey().split("-", 2); int from = Integer.parseInt(split[0]); int to = Integer.parseInt(split[1]); // Special block color handling if (name.contains("%color%")) { for (int i = from; i <= to; i++) { mappings.put(i, new MappedLegacyBlockItem(id, data, name.replace("%color%", BlockColors.get(i - from)), block)); } } else { MappedLegacyBlockItem mappedBlockItem = new MappedLegacyBlockItem(id, data, name, block); for (int i = from; i <= to; i++) { mappings.put(i, mappedBlockItem); } } } else { mappings.put(Integer.parseInt(dataEntry.getKey()), new MappedLegacyBlockItem(id, data, name, block)); } } } } protected LegacyBlockItemRewriter(T protocol) { super(protocol, false); replacementData = LEGACY_MAPPINGS.get(protocol.getClass().getSimpleName().split("To")[1].replace("_", ".")); } @Override @Nullable public Item handleItemToClient(Item item) { if (item == null) return null; MappedLegacyBlockItem data = replacementData.get(item.getIdentifier()); if (data == null) { // Just rewrite the id return super.handleItemToClient(item); } short originalData = item.getData(); item.setIdentifier(data.getId()); // Keep original data if mapped data is set to -1 if (data.getData() != -1) { item.setData(data.getData()); } // Set display name if (data.getName() != null) { if (item.getTag() == null) { item.setTag(new CompoundTag()); } CompoundTag display = item.getTag().get("display"); if (display == null) { item.getTag().put("display", display = new CompoundTag()); } StringTag nameTag = display.get("Name"); if (nameTag == null) { display.put("Name", nameTag = new StringTag(data.getName())); display.put(nbtTagName + "|customName", new ByteTag()); } // Handle colors String value = nameTag.getValue(); if (value.contains("%vb_color%")) { display.put("Name", new StringTag(value.replace("%vb_color%", BlockColors.get(originalData)))); } } return item; } public int handleBlockID(int idx) { int type = idx >> 4; int meta = idx & 15; Block b = handleBlock(type, meta); if (b == null) return idx; return (b.getId() << 4 | (b.getData() & 15)); } @Nullable public Block handleBlock(int blockId, int data) { MappedLegacyBlockItem settings = replacementData.get(blockId); if (settings == null || !settings.isBlock()) return null; Block block = settings.getBlock(); // For some blocks, the data can still be useful (: if (block.getData() == -1) { return block.withData(data); } return block; } protected void handleChunk(Chunk chunk) { // Map Block Entities Map tags = new HashMap<>(); for (CompoundTag tag : chunk.getBlockEntities()) { Tag xTag; Tag yTag; Tag zTag; if ((xTag = tag.get("x")) == null || (yTag = tag.get("y")) == null || (zTag = tag.get("z")) == null) { continue; } Pos pos = new Pos( ((NumberTag) xTag).asInt() & 0xF, ((NumberTag) yTag).asInt(), ((NumberTag) zTag).asInt() & 0xF); tags.put(pos, tag); // Handle given Block Entities ChunkSection section = chunk.getSections()[pos.getY() >> 4]; if (section == null) continue; int block = section.getFlatBlock(pos.getX(), pos.getY() & 0xF, pos.getZ()); int btype = block >> 4; MappedLegacyBlockItem settings = replacementData.get(btype); if (settings != null && settings.hasBlockEntityHandler()) { settings.getBlockEntityHandler().handleOrNewCompoundTag(block, tag); } } for (int i = 0; i < chunk.getSections().length; i++) { ChunkSection section = chunk.getSections()[i]; if (section == null) continue; boolean hasBlockEntityHandler = false; // Map blocks for (int j = 0; j < section.getPaletteSize(); j++) { int block = section.getPaletteEntry(j); int btype = block >> 4; int meta = block & 0xF; Block b = handleBlock(btype, meta); if (b != null) { section.setPaletteEntry(j, (b.getId() << 4) | (b.getData() & 0xF)); } // We already know that is has a handler if (hasBlockEntityHandler) continue; MappedLegacyBlockItem settings = replacementData.get(btype); if (settings != null && settings.hasBlockEntityHandler()) { hasBlockEntityHandler = true; } } if (!hasBlockEntityHandler) continue; // We need to handle a Block Entity :( for (int x = 0; x < 16; x++) { for (int y = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { int block = section.getFlatBlock(x, y, z); int btype = block >> 4; int meta = block & 15; MappedLegacyBlockItem settings = replacementData.get(btype); if (settings == null || !settings.hasBlockEntityHandler()) continue; Pos pos = new Pos(x, (y + (i << 4)), z); // Already handled above if (tags.containsKey(pos)) continue; CompoundTag tag = new CompoundTag(); tag.put("x", new IntTag(x + (chunk.getX() << 4))); tag.put("y", new IntTag(y + (i << 4))); tag.put("z", new IntTag(z + (chunk.getZ() << 4))); settings.getBlockEntityHandler().handleOrNewCompoundTag(block, tag); chunk.getBlockEntities().add(tag); } } } } } protected CompoundTag getNamedTag(String text) { CompoundTag tag = new CompoundTag(); tag.put("display", new CompoundTag()); text = "§r" + text; ((CompoundTag) tag.get("display")).put("Name", new StringTag(jsonNameFormat ? ChatRewriter.legacyTextToJsonString(text) : text)); return tag; } private static final class Pos { private final int x; private final short y; private final int z; private Pos(int x, int y, int z) { this.x = x; this.y = (short) y; this.z = z; } public int getX() { return x; } public int getY() { return y; } public int getZ() { return z; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Pos pos = (Pos) o; if (x != pos.x) return false; if (y != pos.y) return false; return z == pos.z; } @Override public int hashCode() { int result = x; result = 31 * result + y; result = 31 * result + z; return result; } @Override public String toString() { return "Pos{" + "x=" + x + ", y=" + y + ", z=" + z + '}'; } } }