Compare commits

..
28 Commits
Author SHA1 Message Date
Nassim Jahnke 0c01425641 5.3.2 Release 2025-04-13 15:45:15 +02:00
Nassim Jahnke 01d26e7ac5 Make trimStrings public 2025-04-12 15:37:19 +02:00
Nassim Jahnke 99ea6c32ad Add client and server protocol to informative exception data 2025-04-11 22:20:38 +02:00
Nassim Jahnke cf231d92b1 Update mcstructs
Fixes ViaVersion/ViaBackwards#988
2025-04-10 18:58:23 +02:00
FlorianMichael f555188428 [ci skip] Print correct max read length in StringType sanity checks (#4456) 2025-04-10 16:16:46 +02:00
FlorianMichael 93d6fa2e78 Only add (block) entity id if needed for tooltips in 1.20.3->.5 2025-04-09 17:22:33 +02:00
Nassim Jahnke 162fcafb36 Ignore unknown entity data too in 1.20.3->1.20.5
Fixes #4433
Closes #4451
2025-04-09 16:24:03 +02:00
FlorianMichael 3d0a0fe590 Send player input every tick when in boat in 1.21->1.21.2 (#4449) 2025-04-09 16:14:07 +02:00
bookyandFlorianMichael b06556a84e Fix advancement menu background keys (#4454)
Co-authored-by: FlorianMichael <florian.michael07@gmail.com>
2025-04-09 15:56:06 +02:00
Nassim Jahnke ac8bb104b3 Fix primitive array copying 2025-04-08 15:54:02 +02:00
FlorianMichael 4b85ca7691 Fix handling of removed item components in 1.20.3->.5 component rewriter (#4452)
Only relevant for VB though as VV will never put empty structured data through it.
2025-04-08 15:34:30 +02:00
FlorianMichael ebd8eb79c1 Use correct version in json to tag conversion in 1.21.4->.5 (#4450) 2025-04-08 15:32:07 +02:00
Nassim Jahnke 09cffbfb33 Null check connection in Velocity connect listener 2025-04-05 18:35:24 +02:00
FlorianMichael 49a1bffc98 Only send the connection details if ViaVersion is active (#4434) 2025-04-05 11:54:06 +02:00
FlorianMichael da022a0b63 [ci skip] Fix ItemRewriter#registerContainerClick1_21_5 data order (#4435) 2025-04-05 11:53:36 +02:00
Nassim Jahnke 3beddaf522 Move show item container contents conversion to the right place
Fixes #4444
2025-04-05 11:53:08 +02:00
Nassim Jahnke 70139c6838 Bump version to 5.3.2-SNAPSHOT 2025-04-05 11:52:24 +02:00
Nassim Jahnke f040077c13 5.3.1 Release 2025-03-30 21:07:54 +02:00
Nassim Jahnke 1f404820b7 Track it on 1.21.4 servers already 2025-03-30 18:51:03 +02:00
Nassim Jahnke 742e70fff6 Small cleanup, track gamemode 2025-03-30 18:26:05 +02:00
Nassim Jahnke dd6a1a7f6e Update old version warning to <1.16 servers
The original <1.13 warning was added almost 5 years ago, since then more small issues have come up from newer clients having fundamental changes and server stats on those old versions have dropped even more. That said, ViaVersion will keep working on these servers presumably forever
2025-03-30 16:02:27 +02:00
Nassim Jahnke 08ef7c2f35 Add back legacy hover event conversion 2025-03-30 15:05:03 +02:00
Gero e128469313 Fix missing mapping of CHUNKS_BIOMES for 1.21.5 (#4425) 2025-03-30 11:56:29 +02:00
FlorianMichael cbb753271e [ci skip] Update repository declaration in GH workflow (#4424) 2025-03-29 20:28:41 +01:00
FlorianMichael 29f299d882 Update MCStructs to 3.0.0 (#4422) 2025-03-29 14:30:06 +01:00
Pablo Herrera 41138aabd1 Implement pick-block for legacy servers (#4412) 2025-03-29 13:07:44 +01:00
Nassim Jahnke b90f20cfeb Fix 1.20.3 disguised chat handler 2025-03-29 10:15:21 +01:00
Nassim Jahnke 585028beb7 Bump version to 5.3.1-SNAPSHOT 2025-03-29 10:14:45 +01:00
40 changed files with 701 additions and 241 deletions

No files matched your search

+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
run: ./gradlew build
- name: Upload Artifacts to GitHub
# Only upload on the preview repository as there is no CI job for it.
if: ${{ github.repository == 'ViaVersionDev' && github.ref_name == 'preview' }}
if: ${{ github.repository == 'ViaVersion/ViaVersionDev' && github.ref_name == 'preview' }}
uses: actions/upload-artifact@v4
with:
name: Artifacts
+1
View File
@@ -24,6 +24,7 @@ dependencies {
}
implementation(rootProject.libs.text) {
exclude("com.google.code.gson", "gson")
exclude("com.google.guava", "guava")
exclude("com.viaversion", "nbt")
}
api(libs.snakeYaml)
@@ -123,6 +123,20 @@ public interface EntityTracker {
*/
void setClientEntityId(int clientEntityId);
/**
* Returns whether the client player has the "insta build" ability, usually from creative mode.
*
* @return whether the client player can insta build
*/
boolean canInstaBuild();
/**
* Sets whether the client player has the "insta build" ability, usually from creative mode.
*
* @param instaBuild whether the client player can insta build
*/
void setInstaBuild(boolean instaBuild);
/**
* Returns the current world section height (block height / 16).
* This is always 16 for sub 1.17 worlds.
@@ -58,7 +58,7 @@ public class BlockChangeRecord1_16_2 implements BlockChangeRecord {
@Override
public short getY(int chunkSectionY) {
Preconditions.checkArgument(chunkSectionY >= 0, "Invalid chunkSectionY: " + chunkSectionY);
Preconditions.checkArgument(chunkSectionY >= 0, "Invalid chunkSectionY: %s", chunkSectionY);
return (short) ((chunkSectionY << 4) + sectionY);
}
@@ -24,22 +24,28 @@ package com.viaversion.viaversion.api.minecraft;
public enum GameMode {
NOT_SET(""),
SURVIVAL("Survival Mode"),
CREATIVE("Creative Mode"),
ADVENTURE("Adventure Mode"),
SPECTATOR("Spectator Mode");
NOT_SET("", -1),
SURVIVAL("Survival Mode", 0),
CREATIVE("Creative Mode", 1),
ADVENTURE("Adventure Mode", 2),
SPECTATOR("Spectator Mode", 3);
private final String text;
private final int id;
GameMode(String text) {
GameMode(String text, final int id) {
this.text = text;
this.id = id;
}
public String text() {
return text;
}
public int id() {
return id;
}
public static GameMode getById(int id) {
return switch (id) {
case -1 -> NOT_SET;
@@ -36,7 +36,7 @@ public class BitSetType extends Type<BitSet> {
public BitSetType(final int length) {
super(BitSet.class);
this.length = length;
this.bytesLength = -Math.floorDiv(-length, 8); // Ceiled quotient
this.bytesLength = (int) Math.ceil(length / 8D);
}
@Override
@@ -48,8 +48,8 @@ public class BitSetType extends Type<BitSet> {
@Override
public void write(ByteBuf buffer, BitSet object) {
Preconditions.checkArgument(object.length() <= length, "BitSet of length " + object.length() + " larger than max length " + length);
Preconditions.checkArgument(object.length() <= length, "BitSet of length %s larger than max length %s", object.length(), length);
final byte[] bytes = object.toByteArray();
buffer.writeBytes(Arrays.copyOf(bytes, bytesLength));
buffer.writeBytes(bytes.length == bytesLength ? bytes : Arrays.copyOf(bytes, bytesLength));
}
}
@@ -49,13 +49,13 @@ public class StringType extends Type<String> {
int len = Types.VAR_INT.readPrimitive(buffer);
Preconditions.checkArgument(len <= maxLength * MAX_CHAR_UTF_8_LENGTH,
"Cannot receive string longer than Short.MAX_VALUE * " + MAX_CHAR_UTF_8_LENGTH + " bytes (got %s bytes)", len);
"Cannot receive string longer than " + maxLength + " * " + MAX_CHAR_UTF_8_LENGTH + " bytes (got %s bytes)", len);
String string = buffer.toString(buffer.readerIndex(), len, StandardCharsets.UTF_8);
buffer.skipBytes(len);
Preconditions.checkArgument(string.length() <= maxLength,
"Cannot receive string longer than Short.MAX_VALUE characters (got %s bytes)", string.length());
"Cannot receive string longer than " + maxLength + " characters (got %s bytes)", string.length());
return string;
}
@@ -63,7 +63,7 @@ public class StringType extends Type<String> {
@Override
public void write(ByteBuf buffer, String object) {
if (object.length() > maxLength) {
throw new IllegalArgumentException("Cannot send string longer than Short.MAX_VALUE characters (got " + object.length() + " characters)");
throw new IllegalArgumentException("Cannot send string longer than " + maxLength + " characters (got " + object.length() + " characters)");
}
byte[] b = object.getBytes(StandardCharsets.UTF_8);
@@ -0,0 +1,66 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2025 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.type.types.chunk;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
public class ChunkBiomesType1_19_4 extends Type<DataPalette[]> {
private final PaletteTypeBase paletteType;
private final int ySectionCount;
public ChunkBiomesType1_19_4(final int ySectionCount, final int globalPaletteBiomeBits) {
this(ySectionCount, new PaletteType1_18(PaletteType.BIOMES, globalPaletteBiomeBits));
}
protected ChunkBiomesType1_19_4(final int ySectionCount, final PaletteTypeBase paletteType) {
super(DataPalette[].class);
this.paletteType = paletteType;
this.ySectionCount = ySectionCount;
}
@Override
public DataPalette[] read(final ByteBuf buffer) {
final ByteBuf data = buffer.readSlice(Types.VAR_INT.readPrimitive(buffer));
final DataPalette[] sections = new DataPalette[ySectionCount];
for (int i = 0; i < ySectionCount; i++) {
sections[i] = paletteType.read(data);
}
return sections;
}
@Override
public void write(final ByteBuf buffer, final DataPalette[] value) {
int size = 0;
for (final DataPalette biomes : value) {
size += paletteType.serializedSize(biomes);
}
Types.VAR_INT.writePrimitive(buffer, size);
for (final DataPalette biomes : value) {
paletteType.write(buffer, biomes);
}
}
}
@@ -0,0 +1,32 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2025 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.type.types.chunk;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
public class ChunkBiomesType1_21_5 extends ChunkBiomesType1_19_4 {
public ChunkBiomesType1_21_5(final int ySectionCount, final int globalPaletteBiomeBits) {
super(ySectionCount, new PaletteType1_21_5(PaletteType.BIOMES, globalPaletteBiomeBits));
}
}
@@ -30,10 +30,18 @@ public interface Copyable {
} else if (object instanceof Copyable copyable) {
return (T) copyable.copy();
} else if (object.getClass().isArray()) {
final Object[] array = (Object[]) object;
final Object[] copy = (Object[]) Array.newInstance(array.getClass().getComponentType(), array.length);
for (int i = 0; i < array.length; i++) {
copy[i] = copy(array[i]);
final Class<?> componentType = object.getClass().getComponentType();
final int length = Array.getLength(object);
final Object copy = Array.newInstance(componentType, length);
if (componentType.isPrimitive()) {
for (int i = 0; i < length; i++) {
Array.set(copy, i, Array.get(object, i));
}
} else {
// See if we need to copy elements, too
for (int i = 0; i < length; i++) {
Array.set(copy, i, copy(Array.get(object, i)));
}
}
return (T) copy;
} else {
@@ -37,6 +37,16 @@ public final class Key {
return identifier.substring(index + 1);
}
public static String namespace(final String identifier) {
final int index = identifier.indexOf(':');
if (index == -1) {
return "minecraft";
} else if (index == 0) {
return "minecraft";
}
return identifier.substring(0, index);
}
public static String stripMinecraftNamespace(final String identifier) {
if (identifier.startsWith("minecraft:")) {
return identifier.substring(MINECRAFT_NAMESPACE_LENGTH);
@@ -0,0 +1,114 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2025 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.bukkit.util;
import com.viaversion.viaversion.api.Via;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.inventory.ItemStack;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.Method;
import java.util.Random;
import java.util.logging.Level;
public class LegacyBlockToItem {
private static final LegacyBlockToItem INSTANCE;
private static final Random RANDOM = new Random();
static {
LegacyBlockToItem instance = null;
try {
instance = new LegacyBlockToItem();
} catch (ReflectiveOperationException ex) {
Via.getPlatform().getLogger().log(
Level.WARNING,
"Couldn't find reflection methods/fields for pick-block functionality.\n" +
"Pick-block won't work on clients 1.21.4 and newer.", ex);
}
INSTANCE = instance;
}
private final Method GET_WORLD_HANDLE;
private final Method GET_BLOCK_TYPE;
private final Method GET_BLOCK;
private final Method GET_ITEM_FOR_BLOCK;
private final Method GET_DROP_TYPE;
private final Method TO_BUKKIT_ITEM_STACK;
private final Method NEW_BUKKIT_ITEM_STACK;
private final Method SET_POSITION;
private final Object BLOCK_POSITION;
public LegacyBlockToItem() throws ReflectiveOperationException {
Class<?> blockPosition = NMSUtil.nms("BlockPosition");
Class<?> mutableBlockPosition = NMSUtil.nms("BlockPosition$MutableBlockPosition");
Class<?> world = NMSUtil.nms("World");
Class<?> block = NMSUtil.nms("Block");
Class<?> iBlockData = NMSUtil.nms("IBlockData");
Class<?> nmsItem = NMSUtil.nms("Item");
Class<?> nmsItemStack = NMSUtil.nms("ItemStack");
Class<?> craftItemStack = NMSUtil.obc("inventory.CraftItemStack");
GET_WORLD_HANDLE = NMSUtil.obc("CraftWorld").getDeclaredMethod("getHandle");
GET_BLOCK_TYPE = world.getDeclaredMethod("getType", blockPosition);
GET_BLOCK = iBlockData.getDeclaredMethod("getBlock");
GET_ITEM_FOR_BLOCK = block.getDeclaredMethod("i", iBlockData);
GET_ITEM_FOR_BLOCK.setAccessible(true);
GET_DROP_TYPE = block.getDeclaredMethod("getDropType", iBlockData, Random.class, int.class);
TO_BUKKIT_ITEM_STACK = craftItemStack.getDeclaredMethod("asCraftMirror", nmsItemStack);
NEW_BUKKIT_ITEM_STACK = craftItemStack.getDeclaredMethod("asNewCraftStack", nmsItem);
SET_POSITION = mutableBlockPosition.getDeclaredMethod("c", int.class, int.class, int.class);
BLOCK_POSITION = mutableBlockPosition.getConstructor().newInstance();
}
public static LegacyBlockToItem getInstance() {
return INSTANCE;
}
public @Nullable ItemStack blockToItem(final Block block) {
try {
// World nmsWorld = block.getWorld().getHandle();
Object nmsWorld = GET_WORLD_HANDLE.invoke(block.getWorld());
// IBlockData blockData = nmsWorld.get(block.position());
SET_POSITION.invoke(BLOCK_POSITION, block.getX(), block.getY(), block.getZ());
Object blockData = GET_BLOCK_TYPE.invoke(nmsWorld, BLOCK_POSITION);
// Block nmsBlock = blockData.getBlock();
Object nmsBlock = GET_BLOCK.invoke(blockData);
// ItemStack is = nmsBlock.getItemForBlock(blockData);
Object nmsItemStack = GET_ITEM_FOR_BLOCK.invoke(nmsBlock, blockData);
// CraftItemStack.asCraftMirror(nmsItemStack)
ItemStack stack = (ItemStack) TO_BUKKIT_ITEM_STACK.invoke(null, nmsItemStack);
if (stack.getType() != Material.AIR) return stack;
// Couldn't find an item for the block (eg: redstone dust, sugarcane, doors, etc.), use the drop instead
Object nmsItem = GET_DROP_TYPE.invoke(nmsBlock, blockData, RANDOM, 0);
return (ItemStack) NEW_BUKKIT_ITEM_STACK.invoke(null, nmsItem);
} catch (ReflectiveOperationException ex) {
Via.getPlatform().getLogger().log(
Level.WARNING, "Failed when trying to find the right item for pick-block.", ex);
return null;
}
}
}
@@ -192,9 +192,7 @@ public class BukkitViaLoader implements ViaPlatformLoader {
}
}
if (serverProtocolVersion.olderThan(ProtocolVersion.v1_21_4)) {
if (PaperViaInjector.hasMethod(Material.class, "isItem")) {
Via.getManager().getProviders().use(PickItemProvider.class, new BukkitPickItemProvider(plugin));
}
Via.getManager().getProviders().use(PickItemProvider.class, new BukkitPickItemProvider(plugin));
}
// Needs to be enabled on any version with the updated item desync check
@@ -21,6 +21,7 @@ import com.viaversion.viaversion.ViaVersionPlugin;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.bukkit.platform.PaperViaInjector;
import com.viaversion.viaversion.bukkit.util.LegacyBlockToItem;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.provider.PickItemProvider;
import java.util.UUID;
import org.bukkit.Bukkit;
@@ -31,14 +32,17 @@ import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFactory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.BlockStateMeta;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class BukkitPickItemProvider extends PickItemProvider {
private static final boolean HAS_PLACEMENT_MATERIAL_METHOD = PaperViaInjector.hasMethod("org.bukkit.block.data.BlockData", "getPlacementMaterial");
public class BukkitPickItemProvider extends PickItemProvider {
private static final BlockToItem BLOCK_TO_ITEM = BlockToItem.build();
private static final GetStorageContents GET_STORAGE_CONTENT = GetStorageContents.build();
private static final SetInHand SET_IN_HAND = SetInHand.build();
private static final boolean HAS_SPAWN_EGG_METHOD = PaperViaInjector.hasMethod(ItemFactory.class, "getSpawnEgg", EntityType.class);
private static final double BLOCK_RANGE = 4.5 + 1;
private static final double BLOCK_RANGE_SQUARED = BLOCK_RANGE * BLOCK_RANGE;
@@ -68,27 +72,13 @@ public final class BukkitPickItemProvider extends PickItemProvider {
return;
}
final ItemStack item = blockToItem(block, includeData && player.getGameMode() == GameMode.CREATIVE);
if (item != null) {
final ItemStack item = BLOCK_TO_ITEM.apply(block, includeData && player.getGameMode() == GameMode.CREATIVE);
if (item != null && item.getType() != Material.AIR) {
pickItem(player, item);
}
}, player);
}
private @Nullable ItemStack blockToItem(final Block block, final boolean includeData) {
if (HAS_PLACEMENT_MATERIAL_METHOD) {
final ItemStack item = new ItemStack(block.getBlockData().getPlacementMaterial(), 1);
if (includeData && item.getItemMeta() instanceof final BlockStateMeta blockStateMeta) {
blockStateMeta.setBlockState(block.getState());
item.setItemMeta(blockStateMeta);
}
return item;
} else if (block.getType().isItem()) {
return new ItemStack(block.getType(), 1);
}
return null;
}
@Override
public void pickItemFromEntity(final UserConnection connection, final int entityId, final boolean includeData) {
if (!HAS_SPAWN_EGG_METHOD) {
@@ -120,7 +110,8 @@ public final class BukkitPickItemProvider extends PickItemProvider {
private void pickItem(final Player player, final ItemStack item) {
// Find matching item
final PlayerInventory inventory = player.getInventory();
final ItemStack[] contents = inventory.getStorageContents();
// Prior to getStorageContents, getContents worked the same (it ignored armor)
final ItemStack[] contents = GET_STORAGE_CONTENT.apply(inventory);
int sourceSlot = -1;
for (int i = 0; i < contents.length; i++) {
final ItemStack content = contents[i];
@@ -155,7 +146,7 @@ public final class BukkitPickItemProvider extends PickItemProvider {
}
inventory.setItem(emptySlot, heldItem);
inventory.setItemInMainHand(item);
SET_IN_HAND.apply(inventory, item);
}
private void moveToHotbar(final PlayerInventory inventory, final int sourceSlot, final ItemStack[] contents) {
@@ -168,7 +159,7 @@ public final class BukkitPickItemProvider extends PickItemProvider {
final int targetSlot = findEmptyHotbarSlot(inventory, heldSlot);
inventory.setHeldItemSlot(targetSlot);
final ItemStack heldItem = inventory.getItem(targetSlot);
inventory.setItemInMainHand(contents[sourceSlot]);
SET_IN_HAND.apply(inventory, contents[sourceSlot]);
inventory.setItem(sourceSlot, heldItem);
}
@@ -181,4 +172,56 @@ public final class BukkitPickItemProvider extends PickItemProvider {
}
return heldSlot;
}
@FunctionalInterface
private interface BlockToItem {
ItemStack apply(final Block block, final boolean includeData);
static BlockToItem build() {
if (PaperViaInjector.hasMethod("org.bukkit.block.data.BlockData", "getPlacementMaterial")) {
return (block, includeData) -> {
final ItemStack item = new ItemStack(block.getBlockData().getPlacementMaterial(), 1);
if (includeData && item.getItemMeta() instanceof final BlockStateMeta blockStateMeta) {
blockStateMeta.setBlockState(block.getState());
item.setItemMeta(blockStateMeta);
}
return item;
};
} else if (PaperViaInjector.hasMethod(Material.class, "isItem")) {
return (block, includeData) -> new ItemStack(block.getType(), 1);
} else {
LegacyBlockToItem legacy = LegacyBlockToItem.getInstance();
return legacy != null ? (block, includeData) -> legacy.blockToItem(block) : (b, i) -> null;
}
}
}
@FunctionalInterface
private interface GetStorageContents {
ItemStack[] apply(final PlayerInventory inv);
static GetStorageContents build() {
if (PaperViaInjector.hasMethod(Inventory.class, "getStorageContents")) {
return Inventory::getStorageContents;
} else {
return Inventory::getContents;
}
}
}
@FunctionalInterface
private interface SetInHand {
void apply(final PlayerInventory inv, final ItemStack stack);
@SuppressWarnings("deprecation")
static SetInHand build() {
if (PaperViaInjector.hasMethod(PlayerInventory.class, "setItemInMainHand")) {
return PlayerInventory::setItemInMainHand;
} else {
return PlayerInventory::setItemInHand;
}
}
}
}
+2
View File
@@ -2,6 +2,8 @@ dependencies {
api(projects.viaversionApi)
api(rootProject.libs.text) {
exclude("com.google.code.gson", "gson")
exclude("com.google.guava", "guava")
exclude("com.viaversion", "nbt")
}
// Note: If manually starting tests doesn't work for you in IJ, change 'Gradle -> Run Tests Using' to 'IntelliJ IDEA'
@@ -148,7 +148,7 @@ public class ViaManagerImpl implements ViaManager {
platform.getLogger().warning("ViaVersion only supports newer client versions. Use ViaBackwards to allow older versions (ViaRewind for 1.7/1.8) to join.");
platform.getLogger().warning("Get setup help at https://viaversion.com/setup or download ViaBackwards/ViaRewind directly at https://ci.viaversion.com/");
platform.getLogger().warning("Need more help? Join our Discord at https://viaversion.com/discord");
} else if (protocolVersion.highestSupportedProtocolVersion().olderThan(ProtocolVersion.v1_13)) {
} else if (protocolVersion.highestSupportedProtocolVersion().olderThan(ProtocolVersion.v1_16)) {
platform.getLogger().warning("This version of Minecraft is extremely outdated and support for it has reached its end of life. "
+ "You will still be able to run Via on this Minecraft version, but we will prioritize issues with legacy Minecraft versions less. "
+ "Please consider updating to give your players a better experience and to avoid issues that have long been fixed.");
@@ -19,6 +19,7 @@ package com.viaversion.viaversion.connection;
import com.google.gson.JsonObject;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.platform.ViaPlatform;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
@@ -37,7 +38,14 @@ public final class ConnectionDetails {
public static final String APP_CHANNEL = "vv:app_details";
public static void sendConnectionDetails(final UserConnection connection, final String channel) {
final ProtocolVersion nativeVersion = connection.getProtocolInfo().protocolVersion();
final ProtocolInfo protocolInfo = connection.getProtocolInfo();
final ProtocolVersion nativeVersion = protocolInfo.protocolVersion();
final ProtocolVersion serverVersion = protocolInfo.serverProtocolVersion();
if (serverVersion.equals(nativeVersion)) {
// No need to send details if the native version is the same as the server version
return;
}
final String platformName = Via.getPlatform().getPlatformName();
final String platformVersion = Via.getPlatform().getPlatformVersion();
@@ -43,6 +43,7 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis
private String currentWorld;
private int biomesSent = -1;
private Map<String, DimensionData> dimensions = Collections.emptyMap();
private boolean instaBuild;
public EntityTrackerBase(UserConnection connection, @Nullable EntityType playerType) {
this.connection = connection;
@@ -131,6 +132,16 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis
this.clientEntityId = clientEntityId;
}
@Override
public boolean canInstaBuild() {
return instaBuild;
}
@Override
public void setInstaBuild(final boolean instaBuild) {
this.instaBuild = instaBuild;
}
@Override
public int currentWorldSectionHeight() {
return currentWorldSectionHeight;
@@ -263,8 +263,11 @@ public class PacketWrapperImpl implements PacketWrapper {
}
private InformativeException createInformativeException(final Exception cause, final Type<?> type, final int index) {
final ProtocolInfo protocolInfo = this.user().getProtocolInfo();
return new InformativeException(cause)
.set("Packet Type", this.packetType)
.set("Client Protocol", protocolInfo.protocolVersion().getName())
.set("Server Protocol", protocolInfo.serverProtocolVersion().getName())
.set("Index", index)
.set("Type", type.getTypeName())
.set("Data", this.packetValues)
@@ -19,7 +19,6 @@ package com.viaversion.viaversion.protocols.template;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
@@ -42,33 +41,13 @@ final class EntityPacketRewriter1_99 extends EntityRewriter<ClientboundPacket1_2
registerTrackerWithData1_19(ClientboundPackets1_21_2.ADD_ENTITY, EntityTypes1_21_4.FALLING_BLOCK);
registerSetEntityData(ClientboundPackets1_21_2.SET_ENTITY_DATA, /*Types1_OLD_ENTITY_DATA_LIST, */Types1_21_4.ENTITY_DATA_LIST); // Specify old and new entity data list if changed
registerRemoveEntities(ClientboundPackets1_21_2.REMOVE_ENTITIES);
registerPlayerAbilities(ClientboundPackets1_21_2.PLAYER_ABILITIES);
registerGameEvent(ClientboundPackets1_21_2.GAME_EVENT);
registerLogin1_20_5(ClientboundPackets1_21_2.LOGIN);
registerRespawn1_20_5(ClientboundPackets1_21_2.RESPAWN);
final RegistryDataRewriter registryDataRewriter = new RegistryDataRewriter(protocol);
protocol.registerClientbound(ClientboundConfigurationPackets1_21.REGISTRY_DATA, registryDataRewriter::handle);
protocol.registerClientbound(ClientboundPackets1_21_2.LOGIN, wrapper -> {
final int entityId = wrapper.passthrough(Types.INT); // Entity id
wrapper.passthrough(Types.BOOLEAN); // Hardcore
wrapper.passthrough(Types.STRING_ARRAY); // World List
wrapper.passthrough(Types.VAR_INT); // Max players
wrapper.passthrough(Types.VAR_INT); // View distance
wrapper.passthrough(Types.VAR_INT); // Simulation distance
wrapper.passthrough(Types.BOOLEAN); // Reduced debug info
wrapper.passthrough(Types.BOOLEAN); // Show death screen
wrapper.passthrough(Types.BOOLEAN); // Limited crafting
final int dimensionId = wrapper.passthrough(Types.VAR_INT);
final String world = wrapper.passthrough(Types.STRING);
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world);
trackPlayer(wrapper.user(), entityId);
});
protocol.registerClientbound(ClientboundPackets1_21_2.RESPAWN, wrapper -> {
final int dimensionId = wrapper.passthrough(Types.VAR_INT);
final String world = wrapper.passthrough(Types.STRING);
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world); // Tracks world height and name for chunk data and entity (un)tracking
});
}
@Override
@@ -55,9 +55,9 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import net.lenni0451.mcstructs.core.TextFormatting;
import net.lenni0451.mcstructs.text.ATextComponent;
import net.lenni0451.mcstructs.text.Style;
import net.lenni0451.mcstructs.text.TextComponent;
import net.lenni0451.mcstructs.text.TextFormatting;
import net.lenni0451.mcstructs.text.components.TranslationComponent;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -394,7 +394,7 @@ public final class Protocol1_19To1_19_1 extends AbstractProtocol<ClientboundPack
// Add the replacements
final ListTag<StringTag> parameters = tag.getListTag("parameters", StringTag.class);
final List<ATextComponent> arguments = new ArrayList<>();
final List<TextComponent> arguments = new ArrayList<>();
if (parameters != null) {
for (final StringTag element : parameters) {
JsonElement argument = null;
@@ -94,7 +94,7 @@ public final class Protocol1_20_3To1_20_5 extends AbstractProtocol<ClientboundPa
new StatisticsRewriter<>(this).register(ClientboundPackets1_20_3.AWARD_STATS);
componentRewriter.registerComponentPacket(ClientboundPackets1_20_3.SYSTEM_CHAT);
componentRewriter.registerDisguisedChat(ClientboundPackets1_20_3.DISGUISED_CHAT);
componentRewriter.registerComponentPacket(ClientboundPackets1_20_3.DISGUISED_CHAT);
componentRewriter.registerPlayerCombatKill1_20(ClientboundPackets1_20_3.PLAYER_COMBAT_KILL);
// People add item hovers to all sorts of weird places...
@@ -471,21 +471,35 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
CompoundTag entityTag = tag.getCompoundTag("EntityTag");
if (entityTag != null) {
entityTag = entityTag.copy();
// Additional tooltips of items don't validate the entity id in 1.20.4, add the entity id for these back so
// 1.20.5+ parses it correctly (as otherwise not used on the client).
if (entityTag.contains("variant")) {
entityTag.putString("id", "minecraft:painting");
} else if (entityTag.contains("ShowArms")) {
entityTag.putString("id", "minecraft:armor_stand");
}
data.set(StructuredDataKey.ENTITY_DATA, entityTag);
if (entityTag.contains("id")) {
data.set(StructuredDataKey.ENTITY_DATA, entityTag);
}
}
final CompoundTag blockEntityTag = tag.getCompoundTag("BlockEntityTag");
if (blockEntityTag != null) {
final CompoundTag clonedTag = blockEntityTag.copy();
// Same as above
updateBlockEntityTag(connection, data, clonedTag);
CompoundTag spawnData = clonedTag.getCompoundTag("SpawnData");
if (spawnData == null) {
spawnData = clonedTag.getCompoundTag("spawn_data");
}
if (spawnData != null) {
final CompoundTag entity = spawnData.getCompoundTag("entity");
if (entity != null && entity.getString("id") != null) {
addBlockEntityId(clonedTag, clonedTag.contains("SpawnData") ? "mob_spawner" : "trial_spawner");
}
}
// Not always needed, e.g. shields that had the base color in a block entity tag before
if (blockEntityTag.contains("id")) {
if (clonedTag.contains("id")) {
item.dataContainer().set(StructuredDataKey.BLOCK_ENTITY_DATA, clonedTag);
}
}
@@ -1518,7 +1532,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
}
data.set(StructuredDataKey.CONTAINER1_20_5, filteredItems);
addBlockEntityId(tag, "shulker_box"); // Won't happen to the others and doesn't actually have to be correct otherwise
addBlockEntityId(tag, "shulker_box");
}
}
@@ -247,7 +247,7 @@ public class ComponentRewriter1_20_5<C extends ClientboundPacketType> extends Js
if (!data.isEmpty()) {
final CompoundTag components;
try {
components = toTag(connection, data, false);
components = toTag(connection, data);
} catch (final Exception e) {
if (!Via.getConfig().isSuppressConversionWarnings()) {
protocol.getLogger().log(Level.WARNING, "Error writing components in show_item!", e);
@@ -258,7 +258,7 @@ public class ComponentRewriter1_20_5<C extends ClientboundPacketType> extends Js
}
}
public CompoundTag toTag(final UserConnection connection, final Map<StructuredDataKey<?>, StructuredData<?>> data, final boolean empty) {
public CompoundTag toTag(final UserConnection connection, final Map<StructuredDataKey<?>, StructuredData<?>> data) {
final CompoundTag tag = new CompoundTag();
for (final Map.Entry<StructuredDataKey<?>, StructuredData<?>> entry : data.entrySet()) {
final StructuredDataKey<?> key = entry.getKey();
@@ -273,12 +273,8 @@ public class ComponentRewriter1_20_5<C extends ClientboundPacketType> extends Js
final StructuredData<?> value = entry.getValue();
if (value.isEmpty()) {
if (empty) {
// Theoretically not needed here, but we'll keep it for consistency
tag.put("!" + identifier, new CompoundTag());
continue;
}
throw new IllegalArgumentException("Empty structured data: " + identifier);
tag.put("!" + identifier, new CompoundTag());
continue;
}
//noinspection unchecked
@@ -303,11 +299,19 @@ public class ComponentRewriter1_20_5<C extends ClientboundPacketType> extends Js
return list;
}
public StructuredData<?> readFromTag(final UserConnection connection, final String identifier, final Tag tag) {
private StructuredData<?> readFromTag(final UserConnection connection, String identifier, final Tag tag) {
final boolean removed = identifier.startsWith("!");
if (removed) {
identifier = identifier.substring(1);
}
final int id = protocol.getMappingData().getDataComponentSerializerMappings().mappedId(identifier);
Preconditions.checkArgument(id != -1, "Unknown data component: %s", identifier);
final StructuredDataKey<?> key = structuredDataType.key(id);
return readFromTag(connection, key, id, tag);
if (removed) {
return StructuredData.empty(key, id);
} else {
return readFromTag(connection, key, id, tag);
}
}
protected <T> StructuredData<T> readFromTag(final UserConnection connection, final StructuredDataKey<T> key, final int id, final Tag tag) {
@@ -1561,7 +1565,7 @@ public class ComponentRewriter1_20_5<C extends ClientboundPacketType> extends Js
tag.putInt("count", 1);
}
final Map<StructuredDataKey<?>, StructuredData<?>> components = item.dataContainer().data();
final CompoundTag componentsTag = toTag(connection, components, true);
final CompoundTag componentsTag = toTag(connection, components);
if (!componentsTag.isEmpty()) {
tag.put("components", componentsTag);
}
@@ -24,6 +24,7 @@ import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.DimensionData;
import com.viaversion.viaversion.api.minecraft.GameMode;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.minecraft.RegistryEntry;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
@@ -64,7 +65,6 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter<Clientbound
private static final UUID CREATIVE_BLOCK_INTERACTION_RANGE = UUID.fromString("736565d2-e1a7-403d-a3f8-1aeb3e302542");
private static final UUID CREATIVE_ENTITY_INTERACTION_RANGE = UUID.fromString("98491ef6-97b1-4584-ae82-71a8cc85cf73");
private static final int CREATIVE_MODE_ID = 1;
public EntityPacketRewriter1_20_5(final Protocol1_20_3To1_20_5 protocol) {
super(protocol);
@@ -279,7 +279,7 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter<Clientbound
// Handle creative interaction range
final byte gamemode = wrapper.get(Types.BYTE, 0);
if (gamemode == CREATIVE_MODE_ID) {
if (gamemode == GameMode.CREATIVE.id()) {
sendRangeAttributes(wrapper.user(), true);
}
});
@@ -296,7 +296,7 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter<Clientbound
wrapper.passthrough(Types.LONG); // Seed
final byte gamemode = wrapper.passthrough(Types.BYTE);
sendRangeAttributes(wrapper.user(), gamemode == CREATIVE_MODE_ID);
sendRangeAttributes(wrapper.user(), gamemode == GameMode.CREATIVE.id());
});
protocol.registerClientbound(ClientboundPackets1_20_3.UPDATE_MOB_EFFECT, wrapper -> {
@@ -338,8 +338,8 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter<Clientbound
}
// Resend attributes either with their original list or with the creative range modifier added
final float value = wrapper.passthrough(Types.FLOAT);
sendRangeAttributes(wrapper.user(), value == CREATIVE_MODE_ID);
final int value = (int) Math.floor(wrapper.passthrough(Types.FLOAT) + 0.5F);
sendRangeAttributes(wrapper.user(), value == GameMode.CREATIVE.id());
});
}
@@ -26,7 +26,6 @@ import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
@@ -95,25 +94,9 @@ public final class EntityPacketRewriter1_21 extends EntityRewriter<ClientboundPa
jukeboxSongsPacket.send(Protocol1_20_5To1_21.class);
});
protocol.registerClientbound(ClientboundPackets1_20_5.LOGIN, new PacketHandlers() {
@Override
public void register() {
map(Types.INT); // Entity id
map(Types.BOOLEAN); // Hardcore
map(Types.STRING_ARRAY); // World List
map(Types.VAR_INT); // Max players
map(Types.VAR_INT); // View distance
map(Types.VAR_INT); // Simulation distance
map(Types.BOOLEAN); // Reduced debug info
map(Types.BOOLEAN); // Show death screen
map(Types.BOOLEAN); // Limited crafting
map(Types.VAR_INT); // Dimension id
map(Types.STRING); // World
handler(worldDataTrackerHandlerByKey1_20_5(3));
handler(playerTrackerHandler());
handler(wrapper -> wrapper.user().get(EfficiencyAttributeStorage.class)
.onLoginSent(wrapper.get(Types.INT, 0), wrapper.user()));
}
registerLogin1_20_5(ClientboundPackets1_20_5.LOGIN);
protocol.appendClientbound(ClientboundPackets1_20_5.LOGIN, wrapper -> {
wrapper.user().get(EfficiencyAttributeStorage.class).onLoginSent(wrapper.get(Types.INT, 0), wrapper.user());
});
protocol.registerClientbound(ClientboundPackets1_20_5.RESPAWN, wrapper -> {
@@ -178,7 +178,7 @@ public final class BlockItemPacketRewriter1_21_4 extends StructuredItemRewriter<
// and consume time really high, so the eating animation doesn't play
item.dataContainer().set(StructuredDataKey.CONSUMABLE1_21_2,
new Consumable1_21_2(
3600,
3600,
3,
Holder.of(new SoundEvent("minecraft:intentionally_empty", null)),
false,
@@ -77,29 +77,8 @@ public final class EntityPacketRewriter1_21_4 extends EntityRewriter<Clientbound
};
protocol.registerClientbound(ClientboundConfigurationPackets1_21.REGISTRY_DATA, registryDataRewriter::handle);
protocol.registerClientbound(ClientboundPackets1_21_2.LOGIN, wrapper -> {
final int entityId = wrapper.passthrough(Types.INT); // Entity id
wrapper.passthrough(Types.BOOLEAN); // Hardcore
wrapper.passthrough(Types.STRING_ARRAY); // World List
wrapper.passthrough(Types.VAR_INT); // Max players
wrapper.passthrough(Types.VAR_INT); // View distance
wrapper.passthrough(Types.VAR_INT); // Simulation distance
wrapper.passthrough(Types.BOOLEAN); // Reduced debug info
wrapper.passthrough(Types.BOOLEAN); // Show death screen
wrapper.passthrough(Types.BOOLEAN); // Limited crafting
final int dimensionId = wrapper.passthrough(Types.VAR_INT);
final String world = wrapper.passthrough(Types.STRING);
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world);
trackPlayer(wrapper.user(), entityId);
});
protocol.registerClientbound(ClientboundPackets1_21_2.RESPAWN, wrapper -> {
final int dimensionId = wrapper.passthrough(Types.VAR_INT);
final String world = wrapper.passthrough(Types.STRING);
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world); // Tracks world height and name for chunk data and entity (un)tracking
});
registerLogin1_20_5(ClientboundPackets1_21_2.LOGIN);
registerRespawn1_20_5(ClientboundPackets1_21_2.RESPAWN);
protocol.registerServerbound(ServerboundPackets1_21_4.MOVE_VEHICLE, wrapper -> {
wrapper.passthrough(Types.DOUBLE); // X
@@ -31,6 +31,7 @@ import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk1_21_5;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.Heightmap;
import com.viaversion.viaversion.api.minecraft.data.StructuredData;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
@@ -53,8 +54,11 @@ import com.viaversion.viaversion.api.minecraft.item.data.TooltipDisplay;
import com.viaversion.viaversion.api.minecraft.item.data.Unbreakable;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.rewriter.ComponentRewriter;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.chunk.ChunkBiomesType1_19_4;
import com.viaversion.viaversion.api.type.types.chunk.ChunkBiomesType1_21_5;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
@@ -68,6 +72,7 @@ import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacke
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
import com.viaversion.viaversion.rewriter.StructuredItemRewriter;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.Limit;
import com.viaversion.viaversion.util.Unit;
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
@@ -149,6 +154,19 @@ public final class BlockItemPacketRewriter1_21_5 extends StructuredItemRewriter<
wrapper.write(newChunkType, mappedChunk);
});
protocol.registerClientbound(ClientboundPackets1_21_2.CHUNKS_BIOMES, wrapper -> {
final EntityTracker tracker = protocol.getEntityRewriter().tracker(wrapper.user());
final int globalPaletteBiomeBits = ceilLog2(tracker.biomesSent());
final Type<DataPalette[]> biomesType = new ChunkBiomesType1_19_4(tracker.currentWorldSectionHeight(), globalPaletteBiomeBits);
final Type<DataPalette[]> newBiomesType = new ChunkBiomesType1_21_5(tracker.currentWorldSectionHeight(), globalPaletteBiomeBits);
final int size = wrapper.passthrough(Types.VAR_INT);
for (int i = 0; i < size; i++) {
wrapper.passthrough(Types.CHUNK_POSITION);
wrapper.passthroughAndMap(biomesType, newBiomesType);
}
});
protocol.registerClientbound(ClientboundPackets1_21_2.SET_CURSOR_ITEM, this::passthroughClientboundItem);
registerSetPlayerInventory(ClientboundPackets1_21_2.SET_PLAYER_INVENTORY);
registerCooldown1_21_2(ClientboundPackets1_21_2.COOLDOWN);
@@ -158,6 +176,11 @@ public final class BlockItemPacketRewriter1_21_5 extends StructuredItemRewriter<
registerMerchantOffers1_20_5(ClientboundPackets1_21_2.MERCHANT_OFFERS);
protocol.registerServerbound(ServerboundPackets1_21_5.SET_CREATIVE_MODE_SLOT, wrapper -> {
if (!protocol.getEntityRewriter().tracker(wrapper.user()).canInstaBuild()) {
wrapper.cancel();
return;
}
wrapper.passthrough(Types.SHORT); // Slot
final Item item = handleItemToServer(wrapper.user(), wrapper.read(Types1_21_5.LENGTH_PREFIXED_ITEM));
@@ -185,8 +208,41 @@ public final class BlockItemPacketRewriter1_21_5 extends StructuredItemRewriter<
wrapper.write(Types1_21_5.ITEM, handleItemToServer(wrapper.user(), this.convertHashedItemToStructuredItem(wrapper.user(), carriedItem)));
});
registerAdvancements1_20_3(ClientboundPackets1_21_2.UPDATE_ADVANCEMENTS);
protocol.appendClientbound(ClientboundPackets1_21_2.UPDATE_ADVANCEMENTS, wrapper -> {
protocol.registerClientbound(ClientboundPackets1_21_2.UPDATE_ADVANCEMENTS, wrapper -> {
wrapper.passthrough(Types.BOOLEAN); // Reset/clear
int size = wrapper.passthrough(Types.VAR_INT); // Mapping size
for (int i = 0; i < size; i++) {
wrapper.passthrough(Types.STRING); // Identifier
wrapper.passthrough(Types.OPTIONAL_STRING); // Parent
// Display data
if (wrapper.passthrough(Types.BOOLEAN)) {
final Tag title = wrapper.passthrough(Types.TAG);
final Tag description = wrapper.passthrough(Types.TAG);
final ComponentRewriter componentRewriter = protocol.getComponentRewriter();
if (componentRewriter != null) {
componentRewriter.processTag(wrapper.user(), title);
componentRewriter.processTag(wrapper.user(), description);
}
passthroughClientboundItem(wrapper); // Icon
wrapper.passthrough(Types.VAR_INT); // Frame type
int flags = wrapper.passthrough(Types.INT); // Flags
if ((flags & 1) != 0) {
convertClientAsset(wrapper);
}
wrapper.passthrough(Types.FLOAT); // X
wrapper.passthrough(Types.FLOAT); // Y
}
int requirements = wrapper.passthrough(Types.VAR_INT);
for (int array = 0; array < requirements; array++) {
wrapper.passthrough(Types.STRING_ARRAY);
}
wrapper.passthrough(Types.BOOLEAN); // Send telemetry
}
wrapper.passthrough(Types.STRING_ARRAY); // Removed
final int progressSize = wrapper.passthrough(Types.VAR_INT);
for (int i = 0; i < progressSize; i++) {
@@ -223,6 +279,18 @@ public final class BlockItemPacketRewriter1_21_5 extends StructuredItemRewriter<
recipeRewriter.registerPlaceGhostRecipe(ClientboundPackets1_21_2.PLACE_GHOST_RECIPE);
}
private void convertClientAsset(final PacketWrapper wrapper) {
final String background = wrapper.read(Types.STRING);
final String namespace = Key.namespace(background);
final String path = Key.stripNamespace(background);
if (path.startsWith("textures/") && path.endsWith(".png")) {
final String stripped = path.substring("textures/".length(), path.length() - ".png".length());
wrapper.write(Types.STRING, namespace + ":" + stripped);
} else {
wrapper.write(Types.STRING, namespace + ":" + path);
}
}
private void handleBlockEntity(final UserConnection connection, final BlockEntity blockEntity) {
final CompoundTag tag = blockEntity.tag();
if (tag != null && (blockEntity.typeId() == SIGN_BOCK_ENTITY_ID || blockEntity.typeId() == HANGING_SIGN_BOCK_ENTITY_ID)) {
@@ -109,6 +109,19 @@ public final class ComponentRewriter1_21_5 extends JsonNBTComponentRewriter<Clie
handleEnchantments(componentsTag, "enchantments");
handleEnchantments(componentsTag, "stored_enchantments");
// Usual item handling
final CompoundTag useRemainder = TagUtil.getNamespacedCompoundTag(componentsTag, "use_remainder");
if (useRemainder != null) {
handleShowItem(connection, useRemainder);
}
handleContainerContents(connection, componentsTag);
handleItemArrayContents(connection, componentsTag, "bundle_contents");
handleItemArrayContents(connection, componentsTag, "charged_projectiles");
handleWrittenBookContents(connection, componentsTag);
// NO MORE SNBT IN TEXT COMPONENTS
updateUglyJson(componentsTag, connection);
removeDataComponents(componentsTag, StructuredDataKey.INSTRUMENT1_21_2, StructuredDataKey.JUKEBOX_PLAYABLE1_21);
}
@@ -218,22 +231,9 @@ public final class ComponentRewriter1_21_5 extends JsonNBTComponentRewriter<Clie
final CompoundTag componentsTag = compoundContents.getCompoundTag("components");
handleShowItem(connection, compoundContents, componentsTag);
if (componentsTag == null) {
return;
if (componentsTag != null) {
hoverEventTag.put("components", componentsTag);
}
hoverEventTag.put("components", componentsTag);
final CompoundTag useRemainder = TagUtil.getNamespacedCompoundTag(componentsTag, "use_remainder");
if (useRemainder != null) {
handleShowItem(connection, useRemainder);
}
handleContainerContents(connection, componentsTag);
handleItemArrayContents(connection, componentsTag, "bundle_contents");
handleItemArrayContents(connection, componentsTag, "charged_projectiles");
handleWrittenBookContents(connection, componentsTag);
updateUglyJson(componentsTag, connection);
} else if (contentsTag instanceof final StringTag inlinedContents) {
hoverEventTag.put("id", inlinedContents);
}
@@ -267,7 +267,7 @@ public final class ComponentRewriter1_21_5 extends JsonNBTComponentRewriter<Clie
final String actualKey = TagUtil.getNamespacedTagKey(componentsTag, key);
final String json = componentsTag.getString(actualKey);
if (json == null) {
return;
return;
}
componentsTag.put(actualKey, uglyJsonToTag(connection, json));
@@ -299,6 +299,7 @@ public final class ComponentRewriter1_21_5 extends JsonNBTComponentRewriter<Clie
}
public Tag uglyJsonToTag(final UserConnection connection, final String value) {
// Use the same version for deserializing and serializing, as we handle the remaining changes ourselves
final Tag contents = SerializerVersion.V1_21_4.toTag(SerializerVersion.V1_21_4.toComponent(value));
processTag(connection, contents);
return contents;
@@ -56,6 +56,8 @@ public final class EntityPacketRewriter1_21_5 extends EntityRewriter<Clientbound
registerTrackerWithData1_19(ClientboundPackets1_21_2.ADD_ENTITY, EntityTypes1_21_5.FALLING_BLOCK);
registerSetEntityData(ClientboundPackets1_21_2.SET_ENTITY_DATA, Types1_21_4.ENTITY_DATA_LIST, Types1_21_5.ENTITY_DATA_LIST);
registerRemoveEntities(ClientboundPackets1_21_2.REMOVE_ENTITIES);
registerPlayerAbilities(ClientboundPackets1_21_2.PLAYER_ABILITIES);
registerGameEvent(ClientboundPackets1_21_2.GAME_EVENT);
// No more special experience orb add packet
protocol.registerClientbound(ClientboundPackets1_21_2.ADD_EXPERIENCE_ORB, ClientboundPackets1_21_5.ADD_ENTITY, wrapper -> {
@@ -115,30 +117,10 @@ public final class EntityPacketRewriter1_21_5 extends EntityRewriter<Clientbound
wolfSoundVariantsPacket.send(Protocol1_21_4To1_21_5.class);
});
protocol.registerClientbound(ClientboundPackets1_21_2.LOGIN, wrapper -> {
registerRespawn1_20_5(ClientboundPackets1_21_2.RESPAWN);
registerLogin1_20_5(ClientboundPackets1_21_2.LOGIN);
protocol.appendClientbound(ClientboundPackets1_21_2.LOGIN, wrapper -> {
wrapper.user().get(MessageIndexStorage.class).setIndex(0);
final int entityId = wrapper.passthrough(Types.INT); // Entity id
wrapper.passthrough(Types.BOOLEAN); // Hardcore
wrapper.passthrough(Types.STRING_ARRAY); // World List
wrapper.passthrough(Types.VAR_INT); // Max players
wrapper.passthrough(Types.VAR_INT); // View distance
wrapper.passthrough(Types.VAR_INT); // Simulation distance
wrapper.passthrough(Types.BOOLEAN); // Reduced debug info
wrapper.passthrough(Types.BOOLEAN); // Show death screen
wrapper.passthrough(Types.BOOLEAN); // Limited crafting
final int dimensionId = wrapper.passthrough(Types.VAR_INT);
final String world = wrapper.passthrough(Types.STRING);
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world);
trackPlayer(wrapper.user(), entityId);
});
protocol.registerClientbound(ClientboundPackets1_21_2.RESPAWN, wrapper -> {
final int dimensionId = wrapper.passthrough(Types.VAR_INT);
final String world = wrapper.passthrough(Types.STRING);
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world);
});
protocol.registerClientbound(ClientboundPackets1_21_2.SET_PLAYER_TEAM, wrapper -> {
@@ -30,6 +30,7 @@ import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;
@@ -265,20 +266,21 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
});
protocol.registerServerbound(ServerboundPackets1_21_2.PLAYER_INPUT, wrapper -> {
// Previously only used while in a vehicle, now always sent
// Filter them appropriately
if (!wrapper.user().has(ClientVehicleStorage.class)) {
wrapper.cancel();
// Filter them appropriately and always send them when in a vehicle
wrapper.cancel();
final ClientVehicleStorage vehicleStorage = wrapper.user().get(ClientVehicleStorage.class);
if (vehicleStorage == null) {
return;
}
final byte flags = wrapper.read(Types.BYTE);
final boolean left = (flags & 1 << 2) != 0;
final boolean right = (flags & 1 << 3) != 0;
wrapper.write(Types.FLOAT, left ? IMPULSE : (right ? -IMPULSE : 0F));
final float sidewaysMovement = left ? IMPULSE : (right ? -IMPULSE : 0F);
final boolean forward = (flags & 1 << 0) != 0;
final boolean backward = (flags & 1 << 1) != 0;
wrapper.write(Types.FLOAT, forward ? IMPULSE : (backward ? -IMPULSE : 0F));
final float forwardMovement = forward ? IMPULSE : (backward ? -IMPULSE : 0F);
byte updatedFlags = 0;
if ((flags & 1 << 4) != 0) {
@@ -287,7 +289,8 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
if ((flags & 1 << 5) != 0) {
updatedFlags |= 2;
}
wrapper.write(Types.BYTE, updatedFlags);
vehicleStorage.storeMovement(sidewaysMovement, forwardMovement, updatedFlags);
});
protocol.registerClientbound(ClientboundPackets1_21.TELEPORT_ENTITY, ClientboundPackets1_21_2.ENTITY_POSITION_SYNC, wrapper -> {
@@ -346,6 +349,19 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
wrapper.passthrough(Types.FLOAT); // Yaw
wrapper.passthrough(Types.FLOAT); // Pitch
handleOnGround(wrapper);
final ClientVehicleStorage vehicleStorage = wrapper.user().get(ClientVehicleStorage.class);
if (vehicleStorage == null) {
return;
}
wrapper.sendToServer(Protocol1_21To1_21_2.class);
wrapper.cancel();
final PacketWrapper playerInput = wrapper.create(ServerboundPackets1_20_5.PLAYER_INPUT);
playerInput.write(Types.FLOAT, vehicleStorage.sidewaysMovement());
playerInput.write(Types.FLOAT, vehicleStorage.forwardMovement());
playerInput.write(Types.BYTE, vehicleStorage.flags());
playerInput.sendToServer(Protocol1_21To1_21_2.class);
});
protocol.registerServerbound(ServerboundPackets1_21_2.MOVE_PLAYER_STATUS_ONLY, wrapper -> {
final GroundFlagTracker tracker = wrapper.user().get(GroundFlagTracker.class);
@@ -19,5 +19,38 @@ package com.viaversion.viaversion.protocols.v1_21to1_21_2.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
public record ClientVehicleStorage(int vehicleId) implements StorableObject {
public final class ClientVehicleStorage implements StorableObject {
private final int vehicleId;
private float sidewaysMovement;
private float forwardMovement;
private byte flags;
public ClientVehicleStorage(final int vehicleId) {
this.vehicleId = vehicleId;
}
public void storeMovement(final float sidewaysMovement, final float forwardMovement, final byte flags) {
this.sidewaysMovement = sidewaysMovement;
this.forwardMovement = forwardMovement;
this.flags = flags;
}
public int vehicleId() {
return vehicleId;
}
public float sidewaysMovement() {
return sidewaysMovement;
}
public float forwardMovement() {
return forwardMovement;
}
public byte flags() {
return flags;
}
}
@@ -30,6 +30,7 @@ import com.viaversion.viaversion.api.data.Mappings;
import com.viaversion.viaversion.api.data.entity.DimensionData;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.GameMode;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
@@ -364,6 +365,60 @@ public abstract class EntityRewriter<C extends ClientboundPacketType, T extends
registerSetEntityData(packetType, null, dataType);
}
public void registerLogin1_20_5(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
final int entityId = wrapper.passthrough(Types.INT);
wrapper.passthrough(Types.BOOLEAN); // Hardcore
wrapper.passthrough(Types.STRING_ARRAY); // World List
wrapper.passthrough(Types.VAR_INT); // Max players
wrapper.passthrough(Types.VAR_INT); // View distance
wrapper.passthrough(Types.VAR_INT); // Simulation distance
wrapper.passthrough(Types.BOOLEAN); // Reduced debug info
wrapper.passthrough(Types.BOOLEAN); // Show death screen
wrapper.passthrough(Types.BOOLEAN); // Limited crafting
final int dimensionId = wrapper.passthrough(Types.VAR_INT);
final String world = wrapper.passthrough(Types.STRING);
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world);
wrapper.passthrough(Types.LONG); // Seed
final byte gamemode = wrapper.passthrough(Types.BYTE);
tracker(wrapper.user()).setInstaBuild(gamemode == GameMode.CREATIVE.id());
trackPlayer(wrapper.user(), entityId);
});
}
public void registerRespawn1_20_5(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
final int dimensionId = wrapper.passthrough(Types.VAR_INT);
final String world = wrapper.passthrough(Types.STRING);
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world); // Tracks world height and name for chunk data and entity (un)tracking
wrapper.passthrough(Types.LONG); // Seed
final byte gamemode = wrapper.passthrough(Types.BYTE);
tracker(wrapper.user()).setInstaBuild(gamemode == GameMode.CREATIVE.id());
});
}
// Track the insta payer build ability
public void registerPlayerAbilities(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
final byte flags = wrapper.passthrough(Types.BYTE);
tracker(wrapper.user()).setInstaBuild((flags & 1 << 3) != 0);
});
}
public void registerGameEvent(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
final short event = wrapper.passthrough(Types.UNSIGNED_BYTE);
if (event == 3) {
final int value = (int) Math.floor(wrapper.passthrough(Types.FLOAT) + 0.5F);
tracker(wrapper.user()).setInstaBuild(value == GameMode.CREATIVE.id());
}
});
}
public void clearEntities(final UserConnection connection) {
final EntityTracker tracker = tracker(connection);
tracker.clearEntities();
@@ -198,6 +198,12 @@ public class ItemRewriter<C extends ClientboundPacketType, S extends Serverbound
public void registerSetCreativeModeSlot1_21_5(S packetType, Type<Item> lengthPrefixedItemType, Type<Item> mappedLengthPrefixedItemType) {
protocol.registerServerbound(packetType, wrapper -> {
if (!protocol.getEntityRewriter().tracker(wrapper.user()).canInstaBuild()) {
// Mimic server/client behavior
wrapper.cancel();
return;
}
wrapper.passthrough(Types.SHORT); // Slot
passthroughLengthPrefixedItem(wrapper, lengthPrefixedItemType, mappedLengthPrefixedItemType);
});
@@ -249,12 +255,12 @@ public class ItemRewriter<C extends ClientboundPacketType, S extends Serverbound
wrapper.passthrough(Types.SHORT); // Slot
wrapper.passthrough(Types.BYTE); // Button
wrapper.passthrough(Types.VAR_INT); // Mode
passthroughHashedItem(wrapper); // Carried item
final int affectedItems = Limit.max(wrapper.passthrough(Types.VAR_INT), 128);
for (int i = 0; i < affectedItems; i++) {
wrapper.passthrough(Types.SHORT); // Slot
passthroughHashedItem(wrapper);
}
passthroughHashedItem(wrapper); // Carried item
});
}
@@ -25,12 +25,16 @@ import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.Via;
import java.nio.charset.StandardCharsets;
import java.util.logging.Level;
import net.lenni0451.mcstructs.text.ATextComponent;
import net.lenni0451.mcstructs.text.Style;
import net.lenni0451.mcstructs.text.events.hover.AHoverEvent;
import net.lenni0451.mcstructs.text.events.hover.impl.TextHoverEvent;
import net.lenni0451.mcstructs.text.serializer.LegacyStringDeserializer;
import net.lenni0451.mcstructs.text.TextComponent;
import net.lenni0451.mcstructs.text.components.StringComponent;
import net.lenni0451.mcstructs.text.events.hover.HoverEvent;
import net.lenni0451.mcstructs.text.events.hover.impl.EntityHoverEvent;
import net.lenni0451.mcstructs.text.events.hover.impl.ItemHoverEvent;
import net.lenni0451.mcstructs.text.serializer.TextComponentSerializer;
import net.lenni0451.mcstructs.text.stringformat.StringFormat;
import net.lenni0451.mcstructs.text.stringformat.handling.ColorHandling;
import net.lenni0451.mcstructs.text.stringformat.handling.DeserializerUnknownHandling;
import net.lenni0451.mcstructs.text.utils.TextUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -57,7 +61,7 @@ public final class ComponentUtil {
public static @Nullable JsonElement tagToJson(@Nullable final Tag tag) {
try {
final ATextComponent component = SerializerVersion.V1_20_3.toComponent(tag);
final TextComponent component = SerializerVersion.V1_20_3.toComponent(tag);
return component != null ? SerializerVersion.V1_19_4.toJson(component) : null;
} catch (final Exception e) {
if (!Via.getConfig().isSuppressConversionWarnings()) {
@@ -73,7 +77,7 @@ public final class ComponentUtil {
}
try {
final ATextComponent component = SerializerVersion.V1_19_4.toComponent(element);
final TextComponent component = SerializerVersion.V1_19_4.toComponent(element);
return trimStrings(SerializerVersion.V1_20_3.toTag(component));
} catch (final Exception e) {
if (!Via.getConfig().isSuppressConversionWarnings()) {
@@ -83,7 +87,7 @@ public final class ComponentUtil {
}
}
private static Tag trimStrings(final Tag input) {
public static Tag trimStrings(final Tag input) {
// Dirty fix for https://github.com/ViaVersion/ViaVersion/issues/3650
// Usually tripped by hover event data being too long, e.g. book or shulker box contents, which have to be held in full as SNBT
if (input == null) {
@@ -102,7 +106,7 @@ public final class ComponentUtil {
public static @Nullable String tagToJsonString(@Nullable final Tag tag) {
try {
final ATextComponent component = SerializerVersion.V1_20_5.toComponent(tag);
final TextComponent component = SerializerVersion.V1_20_5.toComponent(tag);
return component != null ? SerializerVersion.V1_20_3.toString(component) : null;
} catch (final Exception e) {
if (!Via.getConfig().isSuppressConversionWarnings()) {
@@ -132,27 +136,41 @@ public final class ComponentUtil {
}
public static @Nullable JsonElement convertJsonOrEmpty(@Nullable final String json, final SerializerVersion from, final SerializerVersion to) {
final ATextComponent component = from.toComponent(json);
final TextComponent component = from.toComponent(json);
if (component == null) {
return emptyJsonComponent();
}
return to.toJson(component);
}
private static JsonElement convert(final SerializerVersion from, final SerializerVersion to, final ATextComponent component) {
private static JsonElement convert(final SerializerVersion from, final SerializerVersion to, final TextComponent component) {
if (from.ordinal() >= SerializerVersion.V1_16.ordinal() && to.ordinal() < SerializerVersion.V1_16.ordinal()) {
// Convert hover event to legacy format
final Style style = component.getStyle();
final AHoverEvent hoverEvent = style.getHoverEvent();
if (hoverEvent != null && !(hoverEvent instanceof TextHoverEvent)) {
style.setHoverEvent(hoverEvent.toLegacy(to.jsonSerializer, to.snbtSerializer));
final HoverEvent hoverEvent = style.getHoverEvent();
if (hoverEvent instanceof EntityHoverEvent entityHoverEvent && entityHoverEvent.isModern()) {
final EntityHoverEvent.ModernHolder entityData = entityHoverEvent.asModern();
final CompoundTag tag = new CompoundTag();
tag.putString("type", entityData.getType().get());
tag.putString("id", entityData.getUuid().toString());
tag.putString("name", to.toString(entityData.getName() != null ? entityData.getName() : new StringComponent("")));
entityHoverEvent.setLegacyData(new StringComponent(to.toSNBT(tag)));
} else if (hoverEvent instanceof ItemHoverEvent itemHoverEvent && itemHoverEvent.isModern()) {
final ItemHoverEvent.ModernHolder itemData = itemHoverEvent.asModern();
final CompoundTag tag = new CompoundTag();
tag.putString("id", itemData.getId().get());
tag.putByte("Count", (byte) itemData.getCount());
if (itemData.getTag() != null) {
tag.put("tag", itemData.getTag());
}
itemHoverEvent.setLegacyData(to.toSNBT(tag));
}
}
return to.toJson(component);
}
public static JsonElement legacyToJson(final String message) {
return SerializerVersion.V1_12.toJson(LegacyStringDeserializer.parse(message, true));
return SerializerVersion.V1_12.toJson(StringFormat.vanilla().fromString(message, ColorHandling.RESET, DeserializerUnknownHandling.WHITE));
}
public static String legacyToJsonString(final String message) {
@@ -160,7 +178,7 @@ public final class ComponentUtil {
}
public static String legacyToJsonString(final String message, final boolean itemData) {
final ATextComponent component = LegacyStringDeserializer.parse(message, true);
final TextComponent component = StringFormat.vanilla().fromString(message, ColorHandling.RESET, DeserializerUnknownHandling.WHITE);
if (itemData) {
TextUtils.iterateAll(component, c -> {
if (!c.getStyle().isEmpty()) {
@@ -20,10 +20,10 @@ package com.viaversion.viaversion.util;
import com.google.gson.JsonElement;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.rewriter.text.JsonNBTComponentRewriter;
import net.lenni0451.mcstructs.snbt.SNbtSerializer;
import net.lenni0451.mcstructs.snbt.SNbt;
import net.lenni0451.mcstructs.snbt.exceptions.SNbtDeserializeException;
import net.lenni0451.mcstructs.snbt.exceptions.SNbtSerializeException;
import net.lenni0451.mcstructs.text.ATextComponent;
import net.lenni0451.mcstructs.text.TextComponent;
import net.lenni0451.mcstructs.text.serializer.TextComponentCodec;
import net.lenni0451.mcstructs.text.serializer.TextComponentSerializer;
@@ -36,57 +36,59 @@ import net.lenni0451.mcstructs.text.serializer.TextComponentSerializer;
*/
public enum SerializerVersion {
V1_6(TextComponentSerializer.V1_6, null),
V1_7(TextComponentSerializer.V1_7, SNbtSerializer.V1_7),
V1_8(TextComponentSerializer.V1_8, SNbtSerializer.V1_8),
V1_9(TextComponentSerializer.V1_9, SNbtSerializer.V1_8),
V1_12(TextComponentSerializer.V1_12, SNbtSerializer.V1_12),
V1_13(TextComponentSerializer.V1_12, SNbtSerializer.V1_13),
V1_14(TextComponentSerializer.V1_14, SNbtSerializer.V1_14),
V1_15(TextComponentSerializer.V1_15, SNbtSerializer.V1_14),
V1_16(TextComponentSerializer.V1_16, SNbtSerializer.V1_14),
V1_17(TextComponentSerializer.V1_17, SNbtSerializer.V1_14),
V1_18(TextComponentSerializer.V1_18, SNbtSerializer.V1_14),
V1_19_4(TextComponentSerializer.V1_19_4, SNbtSerializer.V1_14),
V1_20_3(TextComponentCodec.V1_20_3, SNbtSerializer.V1_14),
V1_20_5(TextComponentCodec.V1_20_5, SNbtSerializer.V1_14),
V1_21_4(TextComponentCodec.V1_21_4, SNbtSerializer.V1_14);
V1_7(TextComponentSerializer.V1_7, SNbt.V1_7),
V1_8(TextComponentSerializer.V1_8, SNbt.V1_8),
V1_9(TextComponentSerializer.V1_9, SNbt.V1_8),
V1_12(TextComponentSerializer.V1_12, SNbt.V1_12),
V1_13(TextComponentSerializer.V1_12, SNbt.V1_13),
V1_14(TextComponentSerializer.V1_14, SNbt.V1_14),
V1_15(TextComponentSerializer.V1_15, SNbt.V1_14),
V1_16(TextComponentSerializer.V1_16, SNbt.V1_14),
V1_17(TextComponentSerializer.V1_17, SNbt.V1_14),
V1_18(TextComponentSerializer.V1_18, SNbt.V1_14),
V1_19_4(TextComponentSerializer.V1_19_4, SNbt.V1_14),
V1_20_3(TextComponentCodec.V1_20_3, SNbt.V1_14),
V1_20_5(TextComponentCodec.V1_20_5, SNbt.V1_14),
V1_21_4(TextComponentCodec.V1_21_4, SNbt.V1_14),
V1_21_5(TextComponentCodec.V1_21_5, null/*Currently not needed and also not implemented 100% in MCStructs*/);
final TextComponentSerializer jsonSerializer;
final SNbtSerializer<? extends Tag> snbtSerializer;
final SNbt<? extends Tag> sNbt;
final TextComponentCodec codec;
SerializerVersion(final TextComponentSerializer jsonSerializer, final SNbtSerializer<? extends Tag> snbtSerializer) {
SerializerVersion(final TextComponentSerializer jsonSerializer, final SNbt<? extends Tag> sNbt) {
this.jsonSerializer = jsonSerializer;
this.snbtSerializer = snbtSerializer;
this.sNbt = sNbt;
this.codec = null;
}
SerializerVersion(final TextComponentCodec codec, final SNbtSerializer<? extends Tag> snbtSerializer) {
SerializerVersion(final TextComponentCodec codec, final SNbt<? extends Tag> sNbt) {
this.codec = codec;
this.jsonSerializer = codec.asSerializer();
this.snbtSerializer = snbtSerializer;
this.sNbt = sNbt;
}
public String toString(final ATextComponent component) {
public String toString(final TextComponent component) {
return jsonSerializer.serialize(component);
}
public JsonElement toJson(final ATextComponent component) {
public JsonElement toJson(final TextComponent component) {
return jsonSerializer.serializeJson(component);
}
public Tag toTag(final ATextComponent component) {
public Tag toTag(final TextComponent component) {
if (codec == null) {
throw new IllegalStateException("Cannot convert component to NBT with this version");
}
return codec.serializeNbt(component);
return codec.serializeNbtTree(component);
}
public ATextComponent toComponent(final JsonElement json) {
public TextComponent toComponent(final JsonElement json) {
return jsonSerializer.deserialize(json);
}
public ATextComponent toComponent(final String json) {
public TextComponent toComponent(final String json) {
if (ordinal() >= SerializerVersion.V1_20_3.ordinal()) {
return jsonSerializer.deserializeParser(json);
} else if (ordinal() >= SerializerVersion.V1_9.ordinal()) {
@@ -96,7 +98,7 @@ public enum SerializerVersion {
}
}
public ATextComponent toComponent(final Tag tag) {
public TextComponent toComponent(final Tag tag) {
if (codec == null) {
throw new IllegalStateException("Cannot convert NBT to component with this version");
}
@@ -104,22 +106,24 @@ public enum SerializerVersion {
}
public Tag toTag(final String snbt) {
if (snbtSerializer == null) {
if (sNbt == null) {
throw new IllegalStateException("Cannot convert SNBT to NBT with this version");
}
try {
return snbtSerializer.deserialize(snbt);
return sNbt.deserialize(snbt);
} catch (SNbtDeserializeException e) {
throw new RuntimeException(e);
}
}
public String toSNBT(final Tag tag) {
if (snbtSerializer == null) {
if (sNbt == null) {
throw new IllegalStateException("Cannot convert SNBT to NBT with this version");
}
try {
return snbtSerializer.serialize(tag);
return sNbt.serialize(tag);
} catch (SNbtSerializeException e) {
throw new RuntimeException(e);
}
+2 -2
View File
@@ -1,8 +1,8 @@
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
projectVersion=5.3.0
projectVersion=5.3.2
# Smile emoji
mcVersions=1.21.5,1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9
mcVersions=1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9
mcVersionRange=1.8-1.21.5
velocityVersion=3.4
+2 -2
View File
@@ -5,7 +5,7 @@ metadata.format.version = "1.1"
gson = "2.12.1"
fastutil = "8.5.15"
vianbt = "5.1.0"
mcstructs = "5-2.5.5-SNAPSHOT"
mcstructs = "5-3.1.0-SNAPSHOT"
# Common provided
netty = "4.0.20.Final"
@@ -19,7 +19,7 @@ checkerQual = "3.49.1"
paper = "1.20.4-R0.1-SNAPSHOT"
legacyBukkit = "1.8.8-R0.1-SNAPSHOT"
velocity = "3.1.1"
viaProxy = "3.4.0-SNAPSHOT"
viaProxy = "3.4.2-SNAPSHOT"
[libraries]
@@ -32,8 +32,10 @@ public class ConnectionDetailsListener {
@Subscribe
public void onPostServerJoin(final ServerPostConnectEvent event) {
final UserConnection connection = Via.getAPI().getConnection(event.getPlayer().getUniqueId());
ConnectionDetails.sendConnectionDetails(connection, ConnectionDetails.PROXY_CHANNEL);
final UserConnection connection = Via.getManager().getConnectionManager().getClientConnection(event.getPlayer().getUniqueId());
if (connection != null) {
ConnectionDetails.sendConnectionDetails(connection, ConnectionDetails.PROXY_CHANNEL);
}
}
@Subscribe