/* * 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 . */ package com.viaversion.viaversion.protocols.protocol1_9to1_8; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.viaversion.viaversion.api.minecraft.item.Item; import com.viaversion.viaversion.util.Key; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class ItemRewriter { public static final Map ENTITY_NAME_TO_ID = new HashMap<>(); public static final Map ENTITY_ID_TO_NAME = new HashMap<>(); public static final Map POTION_NAME_TO_ID = new HashMap<>(); public static final Map POTION_ID_TO_NAME = new HashMap<>(); public static final Int2IntMap POTION_INDEX = new Int2IntOpenHashMap(36, .99F); static { /* Entities */ registerEntity(1, "Item"); registerEntity(2, "XPOrb"); registerEntity(7, "ThrownEgg"); registerEntity(8, "LeashKnot"); registerEntity(9, "Painting"); registerEntity(10, "Arrow"); registerEntity(11, "Snowball"); registerEntity(12, "Fireball"); registerEntity(13, "SmallFireball"); registerEntity(14, "ThrownEnderpearl"); registerEntity(15, "EyeOfEnderSignal"); registerEntity(16, "ThrownPotion"); registerEntity(17, "ThrownExpBottle"); registerEntity(18, "ItemFrame"); registerEntity(19, "WitherSkull"); registerEntity(20, "PrimedTnt"); registerEntity(21, "FallingSand"); registerEntity(22, "FireworksRocketEntity"); registerEntity(30, "ArmorStand"); registerEntity(40, "MinecartCommandBlock"); registerEntity(41, "Boat"); registerEntity(42, "MinecartRideable"); registerEntity(43, "MinecartChest"); registerEntity(44, "MinecartFurnace"); registerEntity(45, "MinecartTNT"); registerEntity(46, "MinecartHopper"); registerEntity(47, "MinecartSpawner"); registerEntity(48, "Mob"); registerEntity(49, "Monster"); registerEntity(50, "Creeper"); registerEntity(51, "Skeleton"); registerEntity(52, "Spider"); registerEntity(53, "Giant"); registerEntity(54, "Zombie"); registerEntity(55, "Slime"); registerEntity(56, "Ghast"); registerEntity(57, "PigZombie"); registerEntity(58, "Enderman"); registerEntity(59, "CaveSpider"); registerEntity(60, "Silverfish"); registerEntity(61, "Blaze"); registerEntity(62, "LavaSlime"); registerEntity(63, "EnderDragon"); registerEntity(64, "WitherBoss"); registerEntity(65, "Bat"); registerEntity(66, "Witch"); registerEntity(67, "Endermite"); registerEntity(68, "Guardian"); registerEntity(90, "Pig"); registerEntity(91, "Sheep"); registerEntity(92, "Cow"); registerEntity(93, "Chicken"); registerEntity(94, "Squid"); registerEntity(95, "Wolf"); registerEntity(96, "MushroomCow"); registerEntity(97, "SnowMan"); registerEntity(98, "Ozelot"); registerEntity(99, "VillagerGolem"); registerEntity(100, "EntityHorse"); registerEntity(101, "Rabbit"); registerEntity(120, "Villager"); registerEntity(200, "EnderCrystal"); /* Potions */ registerPotion(-1, "empty"); registerPotion(0, "water"); registerPotion(64, "mundane"); registerPotion(32, "thick"); registerPotion(16, "awkward"); registerPotion(8198, "night_vision"); registerPotion(8262, "long_night_vision"); registerPotion(8206, "invisibility"); registerPotion(8270, "long_invisibility"); registerPotion(8203, "leaping"); registerPotion(8267, "long_leaping"); registerPotion(8235, "strong_leaping"); registerPotion(8195, "fire_resistance"); registerPotion(8259, "long_fire_resistance"); registerPotion(8194, "swiftness"); registerPotion(8258, "long_swiftness"); registerPotion(8226, "strong_swiftness"); registerPotion(8202, "slowness"); registerPotion(8266, "long_slowness"); registerPotion(8205, "water_breathing"); registerPotion(8269, "long_water_breathing"); registerPotion(8261, "healing"); registerPotion(8229, "strong_healing"); registerPotion(8204, "harming"); registerPotion(8236, "strong_harming"); registerPotion(8196, "poison"); registerPotion(8260, "long_poison"); registerPotion(8228, "strong_poison"); registerPotion(8193, "regeneration"); registerPotion(8257, "long_regeneration"); registerPotion(8225, "strong_regeneration"); registerPotion(8201, "strength"); registerPotion(8265, "long_strength"); registerPotion(8233, "strong_strength"); registerPotion(8200, "weakness"); registerPotion(8264, "long_weakness"); } public static void toServer(Item item) { if (item != null) { if (item.identifier() == 383 && item.data() == 0) { // Monster Egg CompoundTag tag = item.tag(); int data = 0; if (tag != null && tag.getCompoundTag("EntityTag") != null) { CompoundTag entityTag = tag.getCompoundTag("EntityTag"); StringTag id = entityTag.getStringTag("id"); if (id != null) { if (ENTITY_NAME_TO_ID.containsKey(id.getValue())) data = ENTITY_NAME_TO_ID.get(id.getValue()); } tag.remove("EntityTag"); } item.setTag(tag); item.setData((short) data); } if (item.identifier() == 373) { // Potion CompoundTag tag = item.tag(); int data = 0; if (tag != null && tag.getStringTag("Potion") != null) { StringTag potion = tag.getStringTag("Potion"); String potionName = Key.stripMinecraftNamespace(potion.getValue()); if (POTION_NAME_TO_ID.containsKey(potionName)) { data = POTION_NAME_TO_ID.get(potionName); } tag.remove("Potion"); } item.setTag(tag); item.setData((short) data); } // Splash potion if (item.identifier() == 438) { CompoundTag tag = item.tag(); int data = 0; item.setIdentifier(373); // Potion if (tag != null && tag.getStringTag("Potion") != null) { StringTag potion = tag.getStringTag("Potion"); String potionName = Key.stripMinecraftNamespace(potion.getValue()); if (POTION_NAME_TO_ID.containsKey(potionName)) { data = POTION_NAME_TO_ID.get(potionName) + 8192; } tag.remove("Potion"); } item.setTag(tag); item.setData((short) data); } boolean newItem = item.identifier() >= 198 && item.identifier() <= 212; newItem |= item.identifier() == 397 && item.data() == 5; newItem |= item.identifier() >= 432 && item.identifier() <= 448; if (newItem) { // Replace server-side unknown items item.setIdentifier(1); item.setData((short) 0); } } } public static void rewriteBookToServer(Item item) { int id = item.identifier(); if (id != 387) { return; } CompoundTag tag = item.tag(); ListTag pages = tag.getListTag("pages", StringTag.class); if (pages == null) { return; } for (int i = 0; i < pages.size(); i++) { StringTag pageTag = pages.get(i); String value = pageTag.getValue(); if (value.replaceAll(" ", "").isEmpty()) { value = "\"" + fixBookSpaceChars(value) + "\""; } else { value = fixBookSpaceChars(value); } pageTag.setValue(value); } } private static String fixBookSpaceChars(String str) { if (!str.startsWith(" ")) { return str; } // hacky but it works :) str = "§r" + str; return str; } public static void toClient(Item item) { if (item != null) { if (item.identifier() == 383 && item.data() != 0) { // Monster Egg CompoundTag tag = item.tag(); if (tag == null) { tag = new CompoundTag(); } CompoundTag entityTag = new CompoundTag(); String entityName = ENTITY_ID_TO_NAME.get((int) item.data()); if (entityName != null) { StringTag id = new StringTag(entityName); entityTag.put("id", id); tag.put("EntityTag", entityTag); } item.setTag(tag); item.setData((short) 0); } if (item.identifier() == 373) { // Potion CompoundTag tag = item.tag(); if (tag == null) { tag = new CompoundTag(); } if (item.data() >= 16384) { item.setIdentifier(438); // splash id item.setData((short) (item.data() - 8192)); } String name = potionNameFromDamage(item.data()); StringTag potion = new StringTag(Key.namespaced(name)); tag.put("Potion", potion); item.setTag(tag); item.setData((short) 0); } if (item.identifier() == 387) { // WRITTEN_BOOK CompoundTag tag = item.tag(); if (tag == null) { tag = new CompoundTag(); } ListTag pages = tag.getListTag("pages", StringTag.class); if (pages == null) { pages = new ListTag<>(Collections.singletonList(new StringTag(Protocol1_9To1_8.fixJson("").toString()))); tag.put("pages", pages); item.setTag(tag); return; } for (int i = 0; i < pages.size(); i++) { StringTag page = pages.get(i); page.setValue(Protocol1_9To1_8.fixJson(page.getValue()).toString()); } item.setTag(tag); } } } public static String potionNameFromDamage(short damage) { String cached = POTION_ID_TO_NAME.get((int) damage); if (cached != null) { return cached; } if (damage == 0) { return "water"; } int effect = damage & 0xF; int name = damage & 0x3F; boolean enhanced = (damage & 0x20) > 0; boolean extended = (damage & 0x40) > 0; boolean canEnhance = true; boolean canExtend = true; String id; switch (effect) { case 1: id = "regeneration"; break; case 2: id = "swiftness"; break; case 3: id = "fire_resistance"; canEnhance = false; break; case 4: id = "poison"; break; case 5: id = "healing"; canExtend = false; break; case 6: id = "night_vision"; canEnhance = false; break; case 8: id = "weakness"; canEnhance = false; break; case 9: id = "strength"; break; case 10: id = "slowness"; canEnhance = false; break; case 11: id = "leaping"; break; case 12: id = "harming"; canExtend = false; break; case 13: id = "water_breathing"; canEnhance = false; break; case 14: id = "invisibility"; canEnhance = false; break; default: canEnhance = false; canExtend = false; switch (name) { case 0: id = "mundane"; break; case 16: id = "awkward"; break; case 32: id = "thick"; break; default: id = "empty"; } } if (effect > 0) { if (canEnhance && enhanced) { id = "strong_" + id; } else if (canExtend && extended) { id = "long_" + id; } } return id; } public static int getNewEffectID(int oldID) { if (oldID >= 16384) { oldID -= 8192; } int index = POTION_INDEX.get(oldID); if (index != -1) { return index; } oldID = POTION_NAME_TO_ID.get(potionNameFromDamage((short) oldID)); return (index = POTION_INDEX.get(oldID)) != -1 ? index : 0; } private static void registerEntity(int id, String name) { ENTITY_ID_TO_NAME.put(id, name); ENTITY_NAME_TO_ID.put(name, id); } private static void registerPotion(int id, String name) { POTION_INDEX.put(id, POTION_ID_TO_NAME.size()); POTION_ID_TO_NAME.put(id, name); POTION_NAME_TO_ID.put(name, id); } }