diff --git a/build.gradle b/build.gradle index 8a16054d7..fa5d53e9a 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,10 @@ repositories { } } +configurations { + testImplementation.extendsFrom compileOnly +} + dependencies { compileOnly "com.viaversion:viaversion-common:5.12.0" compileOnly "org.yaml:snakeyaml:2.7" @@ -98,11 +102,20 @@ dependencies { transitive = false } + testImplementation "org.junit.jupiter:junit-jupiter:6.1.3" + testImplementation "org.mockito:mockito-core:5.24.0" + testImplementation sourceSets.tool.output + testRuntimeOnly "org.junit.platform:junit-platform-launcher:6.1.3" + toolImplementation "org.jsoup:jsoup:1.23.2" toolImplementation "org.ow2.asm:asm-tree:9.11.1" toolImplementation "net.lenni0451.commons:asm:1.9.2" } +test { + useJUnitPlatform() +} + def registerToolTask(String name, String toolMainClass, String toolDescription) { tasks.register(name, JavaExec) { group = "viabedrock tools" diff --git a/src/main/java/net/raphimc/viabedrock/api/chunk/blockstate/JsonBlockStateUpgradeSchema.java b/src/main/java/net/raphimc/viabedrock/api/chunk/blockstate/JsonBlockStateUpgradeSchema.java index 943e631d0..5d12c68f7 100644 --- a/src/main/java/net/raphimc/viabedrock/api/chunk/blockstate/JsonBlockStateUpgradeSchema.java +++ b/src/main/java/net/raphimc/viabedrock/api/chunk/blockstate/JsonBlockStateUpgradeSchema.java @@ -212,7 +212,9 @@ public JsonBlockStateUpgradeSchema(final JsonObject jsonObject) { if (tag.get("states") instanceof CompoundTag states) { for (Pair property : toAdd) { - states.put(property.key(), NbtUtil.createTag(property.value())); + if (!states.contains(property.key())) { + states.put(property.key(), NbtUtil.createTag(property.value())); + } } } }); diff --git a/src/main/java/net/raphimc/viabedrock/netty/CompressionCodec.java b/src/main/java/net/raphimc/viabedrock/netty/CompressionCodec.java index 45f7a6f5a..4ef6cf68d 100644 --- a/src/main/java/net/raphimc/viabedrock/netty/CompressionCodec.java +++ b/src/main/java/net/raphimc/viabedrock/netty/CompressionCodec.java @@ -86,7 +86,8 @@ protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final L } final int rawAlgorithm = in.readUnsignedByte(); - final PacketCompressionAlgorithm algorithm = PacketCompressionAlgorithm.getByValue(rawAlgorithm); + // NetworkSettings uses a uint16 sentinel; a compressed frame has only a uint8 header. + final PacketCompressionAlgorithm algorithm = rawAlgorithm == 0xFF ? PacketCompressionAlgorithm.None : PacketCompressionAlgorithm.getByValue(rawAlgorithm); if (algorithm == null) { // Bedrock client drops the packet if the algorithm is unknown ViaBedrock.getPlatform().getLogger().log(Level.WARNING, "Dropping packet with unknown PacketCompressionAlgorithm: " + rawAlgorithm); in.skipBytes(in.readableBytes()); @@ -95,7 +96,7 @@ protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final L final CompressionAlgorithm compressionAlgorithm = this.getCompressionAlgorithm(algorithm); if (compressionAlgorithm instanceof NoopCompression) { - out.add(in.retain()); + out.add(in.readRetainedSlice(in.readableBytes())); } else { final ByteBuf uncompressedData = ctx.alloc().buffer(); compressionAlgorithm.decompress(in, uncompressedData); // Bedrock client would drop packets with invalid data, but this would be too insane to do diff --git a/src/main/java/net/raphimc/viabedrock/protocol/ClientboundBedrockPackets.java b/src/main/java/net/raphimc/viabedrock/protocol/ClientboundBedrockPackets.java index 1b6df8e3f..2768b1dbc 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/ClientboundBedrockPackets.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/ClientboundBedrockPackets.java @@ -186,10 +186,11 @@ public enum ClientboundBedrockPackets implements ClientboundPacketType { PLAYER_LOCATION(MinecraftPacketIds.PlayerLocation.getValue()), //CAMERA_SPLINE(MinecraftPacketIds.CameraSplinePacket.getValue()), SYNC_WORLD_CLOCKS(MinecraftPacketIds.SyncWorldClocks.getValue()), - VOXEL_SHAPES(MinecraftPacketIds.VoxelShapes.getValue()), + VOXEL_SHAPES(MinecraftPacketIds.VoxelShapesPacket.getValue()), SET_PLAYER_FURNACE_OPTIONS(MinecraftPacketIds.SetPlayerFurnaceOptions.getValue()), - RECORD_STARTED(MinecraftPacketIds.RecordStarted.getValue()); - //STONECUTTER_SET_RECIPE(MinecraftPacketIds.ClientboundStonecutterSetRecipePacket.getValue()); + RECORD_STARTED(MinecraftPacketIds.RecordStarted.getValue()), + SET_PASSENGER_OF_BLOCK(MinecraftPacketIds.SetPassengerOfBlockPacket.getValue()), + PLAY_AUDIO_CONTENT(MinecraftPacketIds.ClientboundPlayAudioContent.getValue()); // UPDATE_SOUND_DATA(MinecraftPacketIds.ClientboundUpdateSoundDataPacket.getValue()); // CONTROL_SCHEME_SET(MinecraftPacketIds.ClientboundControlSchemeSetPacket.getValue()), // PRIMITIVE_SHAPES(MinecraftPacketIds.PrimitiveShapesPacket.getValue()), diff --git a/src/main/java/net/raphimc/viabedrock/protocol/data/JavaRegistries.java b/src/main/java/net/raphimc/viabedrock/protocol/data/JavaRegistries.java index ea3cd9011..d850863cf 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/data/JavaRegistries.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/data/JavaRegistries.java @@ -20,7 +20,6 @@ import com.viaversion.nbt.tag.CompoundTag; import com.viaversion.nbt.tag.ListTag; import com.viaversion.nbt.tag.StringTag; -import com.viaversion.viaversion.libs.fastutil.ints.IntIntPair; import com.viaversion.viaversion.util.Key; import net.raphimc.viabedrock.ViaBedrock; import net.raphimc.viabedrock.api.resourcepack.definition.BiomeDefinitions; @@ -29,6 +28,7 @@ import net.raphimc.viabedrock.protocol.BedrockProtocol; import net.raphimc.viabedrock.protocol.data.enums.Dimension; import net.raphimc.viabedrock.protocol.data.generated.java.RegistryKeys; +import net.raphimc.viabedrock.protocol.model.DimensionDefinition; import net.raphimc.viabedrock.protocol.storage.GameSessionStorage; import net.raphimc.viabedrock.protocol.storage.ResourcePackStorage; @@ -62,13 +62,24 @@ private static void modifyDimensionRegistry(final GameSessionStorage gameSession dimensionRegistry.getCompoundTag(Dimension.OVERWORLD.getKey()).putInt("height", 256); dimensionRegistry.getCompoundTag(Dimension.OVERWORLD.getKey()).putInt("logical_height", 256); } - for (Map.Entry entry : gameSession.getBedrockDimensionDefinitions().entrySet()) { - final CompoundTag dimensionTag = new CompoundTag(); - final int height = entry.getValue().rightInt() - entry.getValue().leftInt(); - dimensionTag.putInt("min_y", entry.getValue().leftInt()); - dimensionTag.putInt("height", height); - dimensionTag.putInt("logical_height", height); - dimensionRegistry.getCompoundTag(entry.getKey()).putAll(dimensionTag); + for (Map.Entry entry : gameSession.getBedrockDimensionDefinitions().entrySet()) { + applyDimensionDefinition(dimensionRegistry.getCompoundTag(entry.getKey()), entry.getValue()); + } + } + + static void applyDimensionDefinition(final CompoundTag dimensionTag, final DimensionDefinition definition) { + dimensionTag.putInt("min_y", definition.minimumY()); + dimensionTag.putInt("height", definition.height()); + dimensionTag.putInt("logical_height", definition.height()); + CompoundTag attributes = dimensionTag.getCompoundTag("attributes"); + if (attributes == null) { + attributes = new CompoundTag(); + dimensionTag.put("attributes", attributes); + } + attributes.putFloat("minecraft:visual/cloud_height", definition.cloudHeight()); + if (!definition.renderClouds()) { + // Java's day timeline multiplies cloud color, preserving zero alpha. + attributes.putString("minecraft:visual/cloud_color", "#00ffffff"); } } diff --git a/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/MinecraftPacketIds.java b/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/MinecraftPacketIds.java index c5bcb0fd3..3d467009c 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/MinecraftPacketIds.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/MinecraftPacketIds.java @@ -217,7 +217,6 @@ public enum MinecraftPacketIds { PlayerVideoCapturePacket(324), PlayerUpdateEntityOverridesPacket(325), PlayerLocation(326), - VoxelShapes(337), SyncWorldClocks(344), SendPartyDestinationCookie(349), PartyDestinationCookieResponse(350), @@ -227,6 +226,29 @@ public enum MinecraftPacketIds { ServerboundMatchmakingCancel(356), ClientboundPlayAudioContent(359), ServerboundRegisterAudioContent(360), + ClientboundControlSchemeSetPacket(327), + PrimitiveShapesPacket(328), + ServerboundPackSettingChangePacket(329), + ClientboundDataStorePacket(330), + GraphicsOverrideParameterPacket(331), + ServerboundDataStorePacket(332), + ClientboundDataDrivenUIShowScreenPacket(333), + ClientboundDataDrivenUICloseScreenPacket(334), + ClientboundDataDrivenUIReloadPacket(335), + ClientboundTextureShiftPacket(336), + VoxelShapesPacket(337), + CameraSplinePacket(338), + ResourcePacksReadyForValidationPacket(340), + LocatorBarPacket(341), + PartyChangedPacket(342), + ServerboundDataDrivenScreenClosedPacket(343), + ClientboundAttributeLayerSyncPacket(345), + ServerStoreInfoPacket(346), + ServerPresenceInfoPacket(347), + ClientboundUpdateSoundDataPacket(348), + ServerboundStonecutterSetRecipePacket(354), + ClientboundStonecutterSetRecipePacket(355), + SetPassengerOfBlockPacket(357), ; private static final Int2ObjectMap BY_VALUE = new Int2ObjectOpenHashMap<>(); diff --git a/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/PacketCompressionAlgorithm.java b/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/PacketCompressionAlgorithm.java index cb4008658..b076111e0 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/PacketCompressionAlgorithm.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/PacketCompressionAlgorithm.java @@ -8,7 +8,7 @@ public enum PacketCompressionAlgorithm { ZLib(0), Snappy(1), - None(255), + None(65535), ; private static final Int2ObjectMap BY_VALUE = new Int2ObjectOpenHashMap<>(); diff --git a/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/ResourcePackResponse.java b/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/ResourcePackResponse.java index 6a4076ea6..c2f6964a6 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/ResourcePackResponse.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/data/enums/bedrock/generated/ResourcePackResponse.java @@ -59,7 +59,7 @@ public static ResourcePackResponse getByName(final String name, final ResourcePa } public int getValue() { - return this.value - 1; // TODO + return this.value; } } diff --git a/src/main/java/net/raphimc/viabedrock/protocol/model/DimensionDefinition.java b/src/main/java/net/raphimc/viabedrock/protocol/model/DimensionDefinition.java new file mode 100644 index 000000000..351421c8f --- /dev/null +++ b/src/main/java/net/raphimc/viabedrock/protocol/model/DimensionDefinition.java @@ -0,0 +1,33 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2023-2026 RK_01/RaphiMC 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 net.raphimc.viabedrock.protocol.model; + +public record DimensionDefinition(int minimumY, int height, int cloudHeight, boolean renderClouds) { + + public DimensionDefinition { + if (height <= 0) { + throw new IllegalArgumentException("Dimension height range must be positive"); + } + Math.addExact(minimumY, height); + } + + public int maximumY() { + return this.minimumY + this.height; + } + +} diff --git a/src/main/java/net/raphimc/viabedrock/protocol/packet/HudPackets.java b/src/main/java/net/raphimc/viabedrock/protocol/packet/HudPackets.java index 74b333318..a15e4a66a 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/packet/HudPackets.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/packet/HudPackets.java @@ -79,7 +79,7 @@ public static void register(final BedrockProtocol protocol) { final long entityUniqueId = wrapper.read(BedrockTypes.VAR_LONG); // actorUniqueId final String name = wrapper.read(BedrockTypes.STRING); // name final String xuid = wrapper.read(BedrockTypes.STRING); // xuid - wrapper.read(BedrockTypes.STRING); // skin playfab id + final String playFabId = wrapper.read(BedrockTypes.STRING); // PlayFab id final String platformOnlineId = wrapper.read(BedrockTypes.STRING); // platform online id final int deviceOs = wrapper.read(BedrockTypes.INT_LE); // device os final SkinData skin = wrapper.read(BedrockTypes.SKIN); // skin @@ -90,6 +90,7 @@ public static void register(final BedrockProtocol protocol) { profiles.add(new GameProfile(name, uuid, new GameProfile.Property[]{ new GameProfile.Property("xuid", xuid), + new GameProfile.Property("playfab_id", playFabId), new GameProfile.Property("platform_online_id", platformOnlineId), new GameProfile.Property("device_os", String.valueOf(deviceOs)), new GameProfile.Property("is_teacher", String.valueOf(isTeacher)), @@ -98,7 +99,6 @@ public static void register(final BedrockProtocol protocol) { })); Via.getManager().getProviders().get(SkinProvider.class).setSkin(wrapper.user(), uuid, skin); - // TODO: Update skin playFabId final Pair entry = playerListStorage.addPlayer(uuid, entityUniqueId, name); if (entry != null) { diff --git a/src/main/java/net/raphimc/viabedrock/protocol/packet/JoinPackets.java b/src/main/java/net/raphimc/viabedrock/protocol/packet/JoinPackets.java index 4f9cc6a83..2e456cd7d 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/packet/JoinPackets.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/packet/JoinPackets.java @@ -31,7 +31,6 @@ import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import com.viaversion.viaversion.api.type.Types; -import com.viaversion.viaversion.libs.fastutil.ints.IntIntImmutablePair; import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets; import com.viaversion.viaversion.protocols.base.v1_7.ClientboundBaseProtocol1_7; import com.viaversion.viaversion.protocols.v26_2to26_3.packet.ClientboundConfigurationPackets26_3; @@ -55,6 +54,7 @@ import net.raphimc.viabedrock.protocol.data.enums.java.Relative; import net.raphimc.viabedrock.protocol.data.enums.java.generated.PlayerInfoUpdateAction; import net.raphimc.viabedrock.protocol.data.generated.java.Attributes; +import net.raphimc.viabedrock.protocol.model.DimensionDefinition; import net.raphimc.viabedrock.protocol.model.*; import net.raphimc.viabedrock.protocol.rewriter.BlockStateRewriter; import net.raphimc.viabedrock.protocol.rewriter.ItemRewriter; @@ -434,16 +434,9 @@ protected void register() { final int count = wrapper.read(BedrockTypes.UNSIGNED_VAR_INT); // entry count for (int i = 0; i < count; i++) { final String dimensionIdentifier = wrapper.read(BedrockTypes.STRING); // dimension identifier - final int minimumHeight = wrapper.read(BedrockTypes.VAR_INT); // minimum height - final int maximumHeight = minimumHeight + wrapper.read(BedrockTypes.VAR_INT); // maximum height - wrapper.read(BedrockTypes.VAR_INT); // generator type - wrapper.read(BedrockTypes.VAR_INT); // dimension type - wrapper.read(BedrockTypes.UUID); // pack id - wrapper.read(BedrockTypes.STRING); // default biome - wrapper.read(BedrockTypes.VAR_INT); // cloud height - wrapper.read(Types.BOOLEAN); // render clouds - if (dimensionIdentifier.equals(Dimension.OVERWORLD.getKey())) { // Bedrock client currently only supports overworld - gameSession.putBedrockDimensionDefinition(dimensionIdentifier, new IntIntImmutablePair(minimumHeight, maximumHeight)); + final DimensionDefinition definition = readDimensionDefinition(wrapper); + if (dimensionIdentifier.equals(Dimension.OVERWORLD.getKey())) { // Only Overworld overrides currently map to Java + gameSession.putBedrockDimensionDefinition(dimensionIdentifier, definition); } } }, State.PLAY, (PacketHandler) PacketWrapper::cancel // Bedrock client ignores dimension data after start game @@ -636,6 +629,18 @@ private static void handleJavaClientGameJoin(final UserConnection user) { setTime.send(BedrockProtocol.class, false); } + static DimensionDefinition readDimensionDefinition(final PacketWrapper wrapper) { + final int minimumY = wrapper.read(BedrockTypes.VAR_INT); + final int heightRange = wrapper.read(BedrockTypes.VAR_INT); + wrapper.read(BedrockTypes.VAR_INT); // generator type + wrapper.read(BedrockTypes.VAR_INT); // dimension type + wrapper.read(BedrockTypes.UUID); // pack id + wrapper.read(BedrockTypes.STRING); // default biome + final int cloudHeight = wrapper.read(BedrockTypes.VAR_INT); + final boolean renderClouds = wrapper.read(Types.BOOLEAN); + return new DimensionDefinition(minimumY, heightRange, cloudHeight, renderClouds); + } + private JoinPackets() { } diff --git a/src/main/java/net/raphimc/viabedrock/protocol/packet/OtherPlayerPackets.java b/src/main/java/net/raphimc/viabedrock/protocol/packet/OtherPlayerPackets.java index 5c11ed0fb..c7ab885fa 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/packet/OtherPlayerPackets.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/packet/OtherPlayerPackets.java @@ -77,8 +77,8 @@ public static void register(final BedrockProtocol protocol) { final PlayerAbilities abilities = wrapper.read(BedrockTypes.PLAYER_ABILITIES); // abilities wrapper.read(BedrockTypes.ENTITY_LINK_ARRAY); // entity links wrapper.read(BedrockTypes.OPTIONAL_PASSENGER_OF_BLOCKS); // passenger of blocks - wrapper.read(BedrockTypes.STRING); // device id - wrapper.read(BedrockTypes.INT_LE); // build platform + final String deviceId = wrapper.read(BedrockTypes.STRING); // device id + final int buildPlatform = wrapper.read(BedrockTypes.INT_LE); // build platform final PlayerEntity entity = entityTracker.addEntity(new PlayerEntity(wrapper.user(), entityRuntimeId, entityTracker.getNextJavaEntityId(), uuid, abilities)); entity.setPosition(position); @@ -92,8 +92,8 @@ public static void register(final BedrockProtocol protocol) { playerInfoUpdate.write(Types.STRING, StringUtil.encodeUuid(uuid)); // username playerInfoUpdate.write(Types.PROFILE_PROPERTY_ARRAY, new GameProfile.Property[]{ new GameProfile.Property("platform_online_id", platformOnlineId), - new GameProfile.Property("device_id", wrapper.read(BedrockTypes.STRING)), // device id - new GameProfile.Property("device_os", wrapper.read(BedrockTypes.INT_LE).toString()) // device os + new GameProfile.Property("device_id", deviceId), + new GameProfile.Property("device_os", Integer.toString(buildPlatform)) }); // properties playerInfoUpdate.write(Types.VAR_INT, GameTypeRewriter.getEffectiveGameMode(gameType, gameSession.getLevelGameType()).ordinal()); // game mode playerInfoUpdate.send(BedrockProtocol.class); diff --git a/src/main/java/net/raphimc/viabedrock/protocol/packet/ResourcePackClientResponse.java b/src/main/java/net/raphimc/viabedrock/protocol/packet/ResourcePackClientResponse.java new file mode 100644 index 000000000..33b4cbde6 --- /dev/null +++ b/src/main/java/net/raphimc/viabedrock/protocol/packet/ResourcePackClientResponse.java @@ -0,0 +1,52 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2023-2026 RK_01/RaphiMC 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 net.raphimc.viabedrock.protocol.packet; + +import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; +import net.raphimc.viabedrock.protocol.data.enums.bedrock.generated.ResourcePackResponse; +import net.raphimc.viabedrock.protocol.types.BedrockTypes; + +public final class ResourcePackClientResponse { + + private ResourcePackClientResponse() { + } + + public static void write(final PacketWrapper wrapper, final ResourcePackResponse status) { + if (status == ResourcePackResponse.Downloading) { + throw new IllegalArgumentException("Downloading requires a pack list"); + } + writeStatus(wrapper, status); + } + + public static void writeDownloading(final PacketWrapper wrapper, final String[] packIds) { + writeStatus(wrapper, ResourcePackResponse.Downloading); + wrapper.write(BedrockTypes.STRING_ARRAY, packIds); + } + + private static void writeStatus(final PacketWrapper wrapper, final ResourcePackResponse status) { + final String name = switch (status) { + case Cancel -> "cancel"; + case Downloading -> "downloading"; + case DownloadingFinished -> "downloadingfinished"; + case ResourcePackStackFinished -> "resourcepackstackfinished"; + }; + wrapper.write(BedrockTypes.UNSIGNED_VAR_INT, status.getValue() - 1); // Cereal variant indexes start at zero, enum values at one. + wrapper.write(BedrockTypes.STRING, name); + } + +} diff --git a/src/main/java/net/raphimc/viabedrock/protocol/packet/ResourcePackPackets.java b/src/main/java/net/raphimc/viabedrock/protocol/packet/ResourcePackPackets.java index dca2f9e71..35b7902bc 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/packet/ResourcePackPackets.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/packet/ResourcePackPackets.java @@ -155,8 +155,7 @@ public static void register(final BedrockProtocol protocol) { if (loadStateTracker == null || !loadStateTracker.hasJavaClientAccepted()) { final PacketWrapper resourcePackClientResponse = wrapper.create(ServerboundBedrockPackets.RESOURCE_PACK_CLIENT_RESPONSE); - resourcePackClientResponse.write(BedrockTypes.UNSIGNED_VAR_INT, ResourcePackResponse.ResourcePackStackFinished.getValue()); // status - resourcePackClientResponse.write(BedrockTypes.STRING, "resourcepackstackfinished"); // #blameMojang + ResourcePackClientResponse.write(resourcePackClientResponse, ResourcePackResponse.ResourcePackStackFinished); resourcePackClientResponse.sendToServer(BedrockProtocol.class); } }); @@ -213,17 +212,14 @@ public static void register(final BedrockProtocol protocol) { if (resourcePackStorage != null) { resourcePackStorage.setLoadedOnJavaClient(); } - wrapper.write(BedrockTypes.UNSIGNED_VAR_INT, ResourcePackResponse.ResourcePackStackFinished.getValue()); // status - wrapper.write(BedrockTypes.STRING, "resourcepackstackfinished"); // #blameMojang + ResourcePackClientResponse.write(wrapper, ResourcePackResponse.ResourcePackStackFinished); } case FAILED_DOWNLOAD, FAILED_RELOAD, DISCARDED -> { ViaBedrock.getPlatform().getLogger().log(Level.WARNING, "Client resource pack download/load failed"); - wrapper.write(BedrockTypes.UNSIGNED_VAR_INT, ResourcePackResponse.ResourcePackStackFinished.getValue()); // status - wrapper.write(BedrockTypes.STRING, "resourcepackstackfinished"); // #blameMojang + ResourcePackClientResponse.write(wrapper, ResourcePackResponse.ResourcePackStackFinished); } case DECLINED, INVALID_URL -> { - wrapper.write(BedrockTypes.UNSIGNED_VAR_INT, ResourcePackResponse.DownloadingFinished.getValue()); // status - wrapper.write(BedrockTypes.STRING, "downloadingfinished"); // #blameMojang + ResourcePackClientResponse.write(wrapper, ResourcePackResponse.DownloadingFinished); } case ACCEPTED -> { final ResourcePackLoadStateTracker loadStateTracker = wrapper.user().get(ResourcePackLoadStateTracker.class); @@ -232,16 +228,14 @@ public static void register(final BedrockProtocol protocol) { loadStateTracker.setJavaClientAccepted(); loadStateTracker.loadRequestedResourcePacks().thenAccept(v -> { final PacketWrapper resourcePackClientResponse = PacketWrapper.create(ServerboundBedrockPackets.RESOURCE_PACK_CLIENT_RESPONSE, wrapper.user()); - resourcePackClientResponse.write(BedrockTypes.UNSIGNED_VAR_INT, ResourcePackResponse.DownloadingFinished.getValue()); // status - resourcePackClientResponse.write(BedrockTypes.STRING, "downloadingfinished"); // #blameMojang + ResourcePackClientResponse.write(resourcePackClientResponse, ResourcePackResponse.DownloadingFinished); resourcePackClientResponse.scheduleSendToServer(BedrockProtocol.class); }).exceptionally(e -> { BedrockProtocol.kickForIllegalState(wrapper.user(), "One of the server resource packs failed to load. Try again later or decline the resource packs.", e); return null; }); } else { - wrapper.write(BedrockTypes.UNSIGNED_VAR_INT, ResourcePackResponse.DownloadingFinished.getValue()); // status - wrapper.write(BedrockTypes.STRING, "downloadingfinished"); // #blameMojang + ResourcePackClientResponse.write(wrapper, ResourcePackResponse.DownloadingFinished); } } case DOWNLOADED -> wrapper.cancel(); diff --git a/src/main/java/net/raphimc/viabedrock/protocol/packet/UnhandledPackets.java b/src/main/java/net/raphimc/viabedrock/protocol/packet/UnhandledPackets.java index 8da27cab0..4159ddcbd 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/packet/UnhandledPackets.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/packet/UnhandledPackets.java @@ -26,6 +26,8 @@ public final class UnhandledPackets { public static void register(final BedrockProtocol protocol) { + protocol.cancelClientbound(ClientboundBedrockPackets.SET_PASSENGER_OF_BLOCK); // Block seating has no Java counterpart yet + protocol.cancelClientbound(ClientboundBedrockPackets.PLAY_AUDIO_CONTENT); // Signed audio content requires the Bedrock content service protocol.cancelClientbound(ClientboundBedrockPackets.SET_HEALTH); // Seems to do nothing meaningful protocol.cancelClientbound(ClientboundBedrockPackets.CAMERA); // Not relevant (Education Edition) protocol.cancelClientbound(ClientboundBedrockPackets.PHOTO_TRANSFER); // Not relevant (Education Edition) diff --git a/src/main/java/net/raphimc/viabedrock/protocol/storage/GameSessionStorage.java b/src/main/java/net/raphimc/viabedrock/protocol/storage/GameSessionStorage.java index 7589ed5ef..b86fa9903 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/storage/GameSessionStorage.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/storage/GameSessionStorage.java @@ -21,10 +21,9 @@ import com.viaversion.nbt.tag.CompoundTag; import com.viaversion.viaversion.api.connection.StoredObject; import com.viaversion.viaversion.api.connection.UserConnection; -import com.viaversion.viaversion.libs.fastutil.ints.IntIntImmutablePair; -import com.viaversion.viaversion.libs.fastutil.ints.IntIntPair; import com.viaversion.viaversion.libs.mcstructs.text.TextComponent; import net.raphimc.viabedrock.protocol.BedrockProtocol; +import net.raphimc.viabedrock.protocol.model.DimensionDefinition; import net.raphimc.viabedrock.protocol.data.JavaRegistries; import net.raphimc.viabedrock.protocol.data.enums.bedrock.generated.ChatRestrictionLevel; import net.raphimc.viabedrock.protocol.data.enums.bedrock.generated.GameType; @@ -38,7 +37,7 @@ public class GameSessionStorage extends StoredObject { private CompoundTag javaRegistries; private CompoundTag bedrockBiomeDefinitions = BedrockProtocol.MAPPINGS.getBedrockBiomeDefinitions(); - private final Map bedrockDimensionDefinitions = new HashMap<>(); + private final Map bedrockDimensionDefinitions = new HashMap<>(); private final Set availableEntityIdentifiers = new HashSet<>(BedrockProtocol.MAPPINGS.getBedrockEntities().keySet()); private Semver bedrockVanillaVersion; private boolean flatGenerator; @@ -57,7 +56,7 @@ public class GameSessionStorage extends StoredObject { public GameSessionStorage(final UserConnection user) { super(user); - this.bedrockDimensionDefinitions.put("minecraft:the_nether", new IntIntImmutablePair(0, 128)); + this.bedrockDimensionDefinitions.put("minecraft:the_nether", new DimensionDefinition(0, 128, 0, false)); } public CompoundTag getJavaRegistries() { @@ -76,11 +75,11 @@ public void setBedrockBiomeDefinitions(final CompoundTag bedrockBiomeDefinitions this.javaRegistries = null; } - public Map getBedrockDimensionDefinitions() { + public Map getBedrockDimensionDefinitions() { return this.bedrockDimensionDefinitions; } - public void putBedrockDimensionDefinition(final String dimensionIdentifier, final IntIntPair dimensionDefinition) { + public void putBedrockDimensionDefinition(final String dimensionIdentifier, final DimensionDefinition dimensionDefinition) { this.bedrockDimensionDefinitions.put(dimensionIdentifier, dimensionDefinition); this.javaRegistries = null; } diff --git a/src/main/java/net/raphimc/viabedrock/protocol/storage/ResourcePackLoadStateTracker.java b/src/main/java/net/raphimc/viabedrock/protocol/storage/ResourcePackLoadStateTracker.java index b0357f63f..fc9bec736 100644 --- a/src/main/java/net/raphimc/viabedrock/protocol/storage/ResourcePackLoadStateTracker.java +++ b/src/main/java/net/raphimc/viabedrock/protocol/storage/ResourcePackLoadStateTracker.java @@ -27,9 +27,8 @@ import net.raphimc.viabedrock.api.resourcepack.http.BedrockPackDownloader; import net.raphimc.viabedrock.protocol.BedrockProtocol; import net.raphimc.viabedrock.protocol.ServerboundBedrockPackets; -import net.raphimc.viabedrock.protocol.data.enums.bedrock.generated.ResourcePackResponse; +import net.raphimc.viabedrock.protocol.packet.ResourcePackClientResponse; import net.raphimc.viabedrock.protocol.provider.ResourcePackProvider; -import net.raphimc.viabedrock.protocol.types.BedrockTypes; import java.net.URL; import java.util.*; @@ -132,9 +131,7 @@ public CompletableFuture loadRequestedResourcePacks() { if (!downloadList.isEmpty()) { ViaBedrock.getPlatform().getLogger().log(Level.INFO, "Downloading " + downloadList.size() + " resource packs over the game protocol"); final PacketWrapper resourcePackClientResponse = PacketWrapper.create(ServerboundBedrockPackets.RESOURCE_PACK_CLIENT_RESPONSE, this.user()); - resourcePackClientResponse.write(BedrockTypes.UNSIGNED_VAR_INT, ResourcePackResponse.Downloading.getValue()); // status - resourcePackClientResponse.write(BedrockTypes.STRING, "downloading"); // #blameMojang - resourcePackClientResponse.write(BedrockTypes.STRING_ARRAY, downloadList.stream().map(ResourcePack.Key::toString).toArray(String[]::new)); // downloading packs + ResourcePackClientResponse.writeDownloading(resourcePackClientResponse, downloadList.stream().map(ResourcePack.Key::toString).toArray(String[]::new)); resourcePackClientResponse.scheduleSendToServer(BedrockProtocol.class); } else { this.loadFuture.complete(null); diff --git a/src/main/resources/assets/viabedrock/resource_packs/chemistry.mcpack b/src/main/resources/assets/viabedrock/resource_packs/chemistry.mcpack index 25865ecb3..77fb59997 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/chemistry.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/chemistry.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/editor.mcpack b/src/main/resources/assets/viabedrock/resource_packs/editor.mcpack index 7199d4b5f..a7006e428 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/editor.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/editor.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/experimental_y_2026_drop_4.mcpack b/src/main/resources/assets/viabedrock/resource_packs/experimental_y_2026_drop_4.mcpack index b9ccd255b..2300de982 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/experimental_y_2026_drop_4.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/experimental_y_2026_drop_4.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla.mcpack index 67d9a53c7..d2f4eb7a2 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.14.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.14.mcpack index c058684ef..673ba971a 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.14.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.14.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.100.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.100.mcpack index dc2d7795e..25fda0de2 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.100.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.100.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.210.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.210.mcpack index e34eb70d4..ff992c486 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.210.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.210.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.mcpack index 382a8a1c8..155919b54 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.16.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.17.0.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.17.0.mcpack index ea280114c..4bda5952c 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.17.0.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.17.0.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.18.0.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.18.0.mcpack index c1546522f..86cfa3ac0 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.18.0.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.18.0.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.18.10.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.18.10.mcpack index 23a0747e7..3a6129f07 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.18.10.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.18.10.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.0.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.0.mcpack index ffdffdc09..77a1ab01e 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.0.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.0.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.10.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.10.mcpack index 56bd2b5e5..9194cbf7d 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.10.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.10.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.20.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.20.mcpack index 355d89c67..630dc7381 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.20.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.20.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.50.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.50.mcpack index fca8539c9..f2c643e40 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.50.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.50.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.60.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.60.mcpack index d2508f28b..f0eb957b6 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.60.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.60.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.80.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.80.mcpack index 386b09a04..e5d740c77 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.80.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.19.80.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.0.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.0.mcpack index c07c8d9c5..2e3a62f02 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.0.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.0.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.20.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.20.mcpack index 36640f74e..6627730bb 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.20.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.20.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.30.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.30.mcpack index d849b8b51..8ada17be1 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.30.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.30.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.80.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.80.mcpack index e6196ee30..da1db98c6 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.80.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.20.80.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.0.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.0.mcpack index 3f31dd90b..5b41b2981 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.0.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.0.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.10.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.10.mcpack index a83e09891..49f3674c8 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.10.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.10.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.100.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.100.mcpack index c1ca6108e..102fbd830 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.100.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.100.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.110.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.110.mcpack index f7e45104e..c5266dcd6 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.110.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.110.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.120.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.120.mcpack index ed4194ae2..2fbd58703 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.120.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.120.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.130.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.130.mcpack index a16d1759f..ef03586ee 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.130.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.130.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.30.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.30.mcpack index 0b9f395bf..738b03603 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.30.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.30.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.40.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.40.mcpack index cd44b7a79..0a68ff3c1 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.40.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.40.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.50.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.50.mcpack index beb15c9c9..3d61dc8ab 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.50.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.50.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.60.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.60.mcpack index 3270bd625..4a3416247 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.60.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.60.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.70.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.70.mcpack index a9906f957..bd59034ef 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.70.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.70.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.80.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.80.mcpack index 9f3c03907..d4aaec267 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.80.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.80.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.90.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.90.mcpack index 686a8660f..e6e2d2bb5 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.90.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.21.90.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.10.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.10.mcpack index 86cbc5fce..1553db886 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.10.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.10.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.30.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.30.mcpack index d987719dc..9ff94cd6a 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.30.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.30.mcpack differ diff --git a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.50.mcpack b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.50.mcpack index f8df445cd..f69190e4d 100644 Binary files a/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.50.mcpack and b/src/main/resources/assets/viabedrock/resource_packs/vanilla_1.26.50.mcpack differ diff --git a/src/test/java/net/raphimc/viabedrock/api/chunk/blockstate/JsonBlockStateUpgradeSchemaTest.java b/src/test/java/net/raphimc/viabedrock/api/chunk/blockstate/JsonBlockStateUpgradeSchemaTest.java new file mode 100644 index 000000000..29133c2bf --- /dev/null +++ b/src/test/java/net/raphimc/viabedrock/api/chunk/blockstate/JsonBlockStateUpgradeSchemaTest.java @@ -0,0 +1,54 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2026 RK_01/RaphiMC and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package net.raphimc.viabedrock.api.chunk.blockstate; + +import com.viaversion.nbt.tag.CompoundTag; +import com.viaversion.viaversion.libs.gson.JsonParser; +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.*; + +final class JsonBlockStateUpgradeSchemaTest { + + @Test + void addsLegacyConnectionsWithoutReplacingCurrentConnections() throws Exception { + final JsonBlockStateUpgradeSchema schema; + try (var input = getClass().getResourceAsStream("/assets/viabedrock/block_state_upgrade_schema/0361_1.26.50_to_1.26.60.json")) { + assertNotNull(input); + schema = new JsonBlockStateUpgradeSchema(JsonParser.parseString(new String(input.readAllBytes(), StandardCharsets.UTF_8)).getAsJsonObject()); + } + for (final String name : new String[]{"minecraft:redstone_wire", "minecraft:fire", "minecraft:chorus_plant"}) { + final var tag = new CompoundTag(); + tag.putString("name", name); + tag.putInt("version", 1 << 24 | 21 << 16 | 60 << 8 | 33); + final var states = new CompoundTag(); + final boolean redstone = name.equals("minecraft:redstone_wire"); + if (redstone) { + states.putString("redstone_north", "up"); + states.putInt("redstone_signal", 12); + } else { + states.putByte("minecraft:connection_north", (byte) 1); + } + tag.put("states", states); + schema.upgrade(tag); + if (redstone) { + assertEquals("up", states.getStringTag("redstone_north").getValue()); + assertEquals("none", states.getStringTag("redstone_east").getValue()); + assertEquals(12, states.getIntTag("redstone_signal").asInt()); + } else { + assertEquals(1, states.getByteTag("minecraft:connection_north").asByte()); + assertEquals(0, states.getByteTag("minecraft:connection_east").asByte()); + assertEquals(name.equals("minecraft:chorus_plant"), states.contains("minecraft:connection_down")); + } + final var once = tag.copy(); + schema.upgrade(tag); + assertEquals(once, tag); + } + } + +} diff --git a/src/test/java/net/raphimc/viabedrock/api/resourcepack/VanillaResourcePackTest.java b/src/test/java/net/raphimc/viabedrock/api/resourcepack/VanillaResourcePackTest.java new file mode 100644 index 000000000..6cc9e1305 --- /dev/null +++ b/src/test/java/net/raphimc/viabedrock/api/resourcepack/VanillaResourcePackTest.java @@ -0,0 +1,38 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2026 RK_01/RaphiMC and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package net.raphimc.viabedrock.api.resourcepack; + +import net.raphimc.viabedrock.api.resourcepack.content.ZipContent; +import net.raphimc.viabedrock.api.resourcepack.definition.BiomeDefinitions; +import net.raphimc.viabedrock.api.resourcepack.definition.FogDefinitions; +import net.raphimc.viabedrock.api.resourcepack.definition.RenderControllerDefinitions; +import net.raphimc.viabedrock.protocol.storage.ResourcePackStorage; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +final class VanillaResourcePackTest { + + @Test + void loadsBiomeFogAndControllerDefinitionsFromBundledPacks() throws Exception { + final var packs = new ArrayList(); + for (final String name : new String[]{"vanilla", "vanilla_1.16.100", "vanilla_1.19.0"}) { + try (var input = getClass().getResourceAsStream("/assets/viabedrock/resource_packs/" + name + ".mcpack")) { + assertNotNull(input); + packs.add(new ResourcePack(new ZipContent(input.readAllBytes()))); + } + } + final var storage = mock(ResourcePackStorage.class); + when(storage.getPackStackBottomToTop()).thenReturn(packs); + assertFalse(new BiomeDefinitions(storage).biomes().isEmpty()); + assertNotNull(new FogDefinitions(storage).get("minecraft:fog_stone_beach")); + assertFalse(new RenderControllerDefinitions(storage).renderControllers().isEmpty()); + } + +} diff --git a/src/test/java/net/raphimc/viabedrock/netty/CompressionCodecTest.java b/src/test/java/net/raphimc/viabedrock/netty/CompressionCodecTest.java new file mode 100644 index 000000000..3893779b5 --- /dev/null +++ b/src/test/java/net/raphimc/viabedrock/netty/CompressionCodecTest.java @@ -0,0 +1,77 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2023-2026 RK_01/RaphiMC 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 net.raphimc.viabedrock.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import net.raphimc.viabedrock.protocol.data.enums.bedrock.generated.PacketCompressionAlgorithm; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +final class CompressionCodecTest { + + @Test + void consumesAnUncompressedFrameWithoutDecodingItsPayloadAgain() { + final var channel = new EmbeddedChannel(new CompressionCodec(PacketCompressionAlgorithm.None, 0)); + try { + assertTrue(channel.writeInbound(Unpooled.wrappedBuffer(new byte[]{(byte) 0xFF, 1, 3, 5, 7}))); + final ByteBuf decoded = channel.readInbound(); + try { + assertEquals(4, decoded.readableBytes()); + assertEquals(0x01030507, decoded.readInt()); + } finally { + decoded.release(); + } + assertNull(channel.readInbound()); + } finally { + channel.finishAndReleaseAll(); + } + } + + @Test + void readsTheByteFrameSentinelAfterNegotiatingTheUnsignedShortSentinel() { + final ByteBuf settings = Unpooled.wrappedBuffer(new byte[]{(byte) 0xFF, (byte) 0xFF}); + final PacketCompressionAlgorithm negotiated = PacketCompressionAlgorithm.getByValue(settings.readUnsignedShortLE()); + settings.release(); + final var channel = new EmbeddedChannel(new CompressionCodec(negotiated, 0)); + try { + assertTrue(channel.writeInbound(Unpooled.wrappedBuffer(new byte[]{(byte) 0xFF, 1, 3, 5, 7}))); + final ByteBuf decoded = channel.readInbound(); + try { + assertEquals(4, decoded.readableBytes()); + assertEquals(0x01030507, decoded.readInt()); + } finally { + decoded.release(); + } + assertTrue(channel.writeOutbound(Unpooled.wrappedBuffer(new byte[]{2, 4, 6, 8}))); + final ByteBuf encoded = channel.readOutbound(); + try { + assertEquals(5, encoded.readableBytes()); + assertEquals(0xFF, encoded.readUnsignedByte()); + assertEquals(0x02040608, encoded.readInt()); + } finally { + encoded.release(); + } + } finally { + channel.finishAndReleaseAll(); + } + } + +} diff --git a/src/test/java/net/raphimc/viabedrock/protocol/data/JavaDimensionRegistryTest.java b/src/test/java/net/raphimc/viabedrock/protocol/data/JavaDimensionRegistryTest.java new file mode 100644 index 000000000..06add2ea7 --- /dev/null +++ b/src/test/java/net/raphimc/viabedrock/protocol/data/JavaDimensionRegistryTest.java @@ -0,0 +1,55 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2026 RK_01/RaphiMC and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package net.raphimc.viabedrock.protocol.data; + +import com.viaversion.nbt.tag.CompoundTag; +import net.raphimc.viabedrock.protocol.model.DimensionDefinition; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class JavaDimensionRegistryTest { + + @Test + void projectsServerBoundsAndCloudHeightWithoutReplacingOtherAttributes() { + final var registry = registry(); + final var dimension = registry.getCompoundTag("minecraft:overworld"); + JavaRegistries.applyDimensionDefinition(dimension, new DimensionDefinition(-64, 384, 96, true)); + assertEquals(-64, dimension.getInt("min_y")); + assertEquals(384, dimension.getInt("height")); + assertEquals(384, dimension.getInt("logical_height")); + final var attributes = dimension.getCompoundTag("attributes"); + assertEquals(96F, attributes.getFloat("minecraft:visual/cloud_height")); + assertEquals("#ccffffff", attributes.getString("minecraft:visual/cloud_color")); + assertEquals("#123456", attributes.getString("minecraft:visual/fog_color")); + } + + @Test + void suppressesCloudsWithTransparentColorAndAcceptsAnAbsentAttributeMap() { + for (final boolean attributesPresent : new boolean[]{false, true}) { + final var registry = registry(); + final var dimension = registry.getCompoundTag("minecraft:overworld"); + if (!attributesPresent) { + dimension.remove("attributes"); + } + JavaRegistries.applyDimensionDefinition(dimension, new DimensionDefinition(0, 256, 128, false)); + assertEquals("#00ffffff", dimension.getCompoundTag("attributes").getString("minecraft:visual/cloud_color")); + assertEquals(128F, dimension.getCompoundTag("attributes").getFloat("minecraft:visual/cloud_height")); + } + } + + private static CompoundTag registry() { + final var registry = new CompoundTag(); + final var dimension = new CompoundTag(); + final var attributes = new CompoundTag(); + attributes.putString("minecraft:visual/cloud_color", "#ccffffff"); + attributes.putString("minecraft:visual/fog_color", "#123456"); + dimension.put("attributes", attributes); + registry.put("minecraft:overworld", dimension); + return registry; + } + +} diff --git a/src/test/java/net/raphimc/viabedrock/protocol/packet/DimensionDataPacketTest.java b/src/test/java/net/raphimc/viabedrock/protocol/packet/DimensionDataPacketTest.java new file mode 100644 index 000000000..bc354000a --- /dev/null +++ b/src/test/java/net/raphimc/viabedrock/protocol/packet/DimensionDataPacketTest.java @@ -0,0 +1,96 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2026 RK_01/RaphiMC and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package net.raphimc.viabedrock.protocol.packet; + +import com.viaversion.viaversion.protocol.packet.PacketWrapperImpl; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.raphimc.viabedrock.protocol.ClientboundBedrockPackets; +import net.raphimc.viabedrock.protocol.types.BedrockTypes; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class DimensionDataPacketTest { + + @Test + void retainsBoundsAndCloudSettingsWithoutConsumingTheNextDefinition() { + for (final int[] range : new int[][]{{0, 256}, {-64, 384}, {32, 64}}) { + for (final boolean clouds : new boolean[]{false, true}) { + final ByteBuf buffer = definition(range[0], range[1], -32, clouds); + try { + buffer.writeByte(77); + final var wrapper = new PacketWrapperImpl(ClientboundBedrockPackets.DIMENSION_DATA, buffer, null); + final var decoded = JoinPackets.readDimensionDefinition(wrapper); + assertEquals(range[0], decoded.minimumY()); + assertEquals(range[1], decoded.height()); + assertEquals(range[0] + range[1], decoded.maximumY()); + assertEquals(-32, decoded.cloudHeight()); + assertEquals(clouds, decoded.renderClouds()); + assertEquals(77, buffer.readUnsignedByte()); + assertFalse(buffer.isReadable()); + } finally { + buffer.release(); + } + } + } + } + + @Test + void rejectsEmptyAndNegativeHeightRanges() { + for (final int range : new int[]{0, -16, Integer.MIN_VALUE}) { + final ByteBuf buffer = definition(-64, range, 192, true); + try { + final var wrapper = new PacketWrapperImpl(ClientboundBedrockPackets.DIMENSION_DATA, buffer, null); + assertThrows(IllegalArgumentException.class, () -> JoinPackets.readDimensionDefinition(wrapper)); + } finally { + buffer.release(); + } + } + } + + @Test + void rejectsUpperBoundOverflowInsteadOfWrappingTheDimension() { + final ByteBuf buffer = definition(Integer.MAX_VALUE - 15, 16, 0, false); + try { + final var wrapper = new PacketWrapperImpl(ClientboundBedrockPackets.DIMENSION_DATA, buffer, null); + assertThrows(ArithmeticException.class, () -> JoinPackets.readDimensionDefinition(wrapper)); + } finally { + buffer.release(); + } + } + + @Test + void retainsRepresentableBoundsAtTheSignedIntegerLimits() { + for (final int minimum : new int[]{Integer.MIN_VALUE, Integer.MAX_VALUE - 16}) { + final ByteBuf buffer = definition(minimum, 16, 128, true); + try { + final var wrapper = new PacketWrapperImpl(ClientboundBedrockPackets.DIMENSION_DATA, buffer, null); + final var decoded = JoinPackets.readDimensionDefinition(wrapper); + assertEquals(minimum, decoded.minimumY()); + assertEquals(minimum + 16, decoded.maximumY()); + } finally { + buffer.release(); + } + } + } + + private static ByteBuf definition(final int minimum, final int range, final int cloudHeight, final boolean clouds) { + final ByteBuf buffer = Unpooled.buffer(); + BedrockTypes.VAR_INT.writePrimitive(buffer, minimum); + BedrockTypes.VAR_INT.writePrimitive(buffer, range); + BedrockTypes.VAR_INT.writePrimitive(buffer, 1); // generator + BedrockTypes.VAR_INT.writePrimitive(buffer, 0); // dimension type + BedrockTypes.UUID.write(buffer, UUID.randomUUID()); + BedrockTypes.STRING.write(buffer, "minecraft:plains"); + BedrockTypes.VAR_INT.writePrimitive(buffer, cloudHeight); + buffer.writeBoolean(clouds); + return buffer; + } + +} diff --git a/src/test/java/net/raphimc/viabedrock/protocol/packet/OtherPlayerPacketsTest.java b/src/test/java/net/raphimc/viabedrock/protocol/packet/OtherPlayerPacketsTest.java new file mode 100644 index 000000000..18c4c1c58 --- /dev/null +++ b/src/test/java/net/raphimc/viabedrock/protocol/packet/OtherPlayerPacketsTest.java @@ -0,0 +1,144 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2026 RK_01/RaphiMC and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package net.raphimc.viabedrock.protocol.packet; + +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.api.data.MappingData; +import com.viaversion.viaversion.api.data.FullMappings; +import com.viaversion.viaversion.api.minecraft.entities.EntityTypes26_3; +import com.viaversion.viaversion.api.protocol.Protocol; +import com.viaversion.viaversion.api.connection.ProtocolInfo; +import com.viaversion.viaversion.api.minecraft.BlockPosition; +import com.viaversion.viaversion.api.minecraft.GameProfile; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey; +import com.viaversion.viaversion.api.minecraft.item.StructuredItem; +import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; +import com.viaversion.viaversion.api.protocol.remapper.PacketHandler; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import com.viaversion.viaversion.api.type.Types; +import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap; +import com.viaversion.viaversion.protocol.packet.PacketWrapperImpl; +import com.viaversion.viaversion.protocols.v26_2to26_3.packet.ClientboundPackets26_3; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.raphimc.viabedrock.api.model.entity.PlayerEntity; +import net.raphimc.viabedrock.protocol.BedrockProtocol; +import net.raphimc.viabedrock.protocol.ClientboundBedrockPackets; +import net.raphimc.viabedrock.protocol.data.enums.bedrock.generated.GameType; +import net.raphimc.viabedrock.protocol.data.enums.bedrock.generated.PassengerOfBlockArguments_EmoteType; +import net.raphimc.viabedrock.protocol.model.BedrockItem; +import net.raphimc.viabedrock.protocol.model.PassengerOfBlocks; +import net.raphimc.viabedrock.protocol.model.Position3f; +import net.raphimc.viabedrock.protocol.rewriter.ItemRewriter; +import net.raphimc.viabedrock.protocol.storage.EntityTracker; +import net.raphimc.viabedrock.protocol.storage.GameSessionStorage; +import net.raphimc.viabedrock.protocol.types.BedrockTypes; +import net.raphimc.viabedrock.protocol.types.item.NetworkItemStackDescriptorType; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +final class OtherPlayerPacketsTest { + + @Test + void preservesDeviceIdentityAndConsumesThePayloadOnce() throws Exception { + // Initialize the component keys before VersionedTypes enters its dependent initializers. + Class.forName(StructuredDataKey.class.getName()); + final var typeProtocol = mock(Protocol.class); + final var mappingData = mock(MappingData.class); + final var entityMappings = mock(FullMappings.class); + when(typeProtocol.getMappingData()).thenReturn(mappingData); + when(mappingData.getEntityMappings()).thenReturn(entityMappings); + int nextId = 0; + for (final var type : EntityTypes26_3.values()) { + if (!type.isAbstractType()) { + when(entityMappings.mappedId(type.identifier())).thenReturn(nextId++); + } + } + when(entityMappings.mappedSize()).thenReturn(nextId); + EntityTypes26_3.initialize(typeProtocol); + for (final boolean seated : new boolean[]{false, true}) { + check(seated, "", 0); + check(seated, "5fa30b41-7d50-4b40-945b-977899244996", 7); + } + } + + private static void check(final boolean seated, final String deviceId, final int platform) throws Exception { + final var protocol = mock(BedrockProtocol.class); + OtherPlayerPackets.register(protocol); + final var handler = ArgumentCaptor.forClass(PacketHandler.class); + verify(protocol).registerClientbound(eq(ClientboundBedrockPackets.ADD_PLAYER), eq(ClientboundPackets26_3.ADD_ENTITY), handler.capture()); + + final var user = mock(UserConnection.class); + final var info = mock(ProtocolInfo.class); + when(user.getProtocolInfo()).thenReturn(info); + when(info.protocolVersion()).thenReturn(ProtocolVersion.v1_21); + when(info.serverProtocolVersion()).thenReturn(ProtocolVersion.v1_21); + final var session = mock(GameSessionStorage.class); + final var items = mock(ItemRewriter.class); + final var entities = mock(EntityTracker.class); + final var player = mock(PlayerEntity.class); + final var itemType = new NetworkItemStackDescriptorType(-1, new Int2ObjectOpenHashMap<>(), false); + when(user.get(GameSessionStorage.class)).thenReturn(session); + when(user.get(ItemRewriter.class)).thenReturn(items); + when(user.get(EntityTracker.class)).thenReturn(entities); + when(session.getLevelGameType()).thenReturn(GameType.Survival); + when(items.newItemType()).thenReturn(itemType); + when(items.javaItem(any(BedrockItem.class))).thenReturn(StructuredItem.empty()); + when(entities.addEntity(any(PlayerEntity.class))).thenReturn(player); + + final ByteBuf buffer = Unpooled.buffer(); + try { + BedrockTypes.UUID.write(buffer, UUID.fromString("8695b182-22a8-4c88-b4ba-07d33e903c28")); + BedrockTypes.STRING.write(buffer, "OtherPlayer"); + BedrockTypes.UNSIGNED_VAR_LONG.write(buffer, 42L); + BedrockTypes.STRING.write(buffer, "platform-chat"); + BedrockTypes.POSITION_3F.write(buffer, new Position3f(1F, 65F, -2F)); + BedrockTypes.POSITION_3F.write(buffer, Position3f.ZERO); + BedrockTypes.POSITION_3F.write(buffer, Position3f.ZERO); + itemType.write(buffer, new BedrockItem((short) 0)); + BedrockTypes.VAR_INT.write(buffer, GameType.Survival.getValue()); + buffer.writeByte(0); // metadata count + buffer.writeByte(0); // integer property count + buffer.writeByte(0); // float property count + buffer.writeLongLE(42L).writeByte(0).writeByte(0).writeByte(0); // abilities, no layers + buffer.writeByte(0); // actor link count + BedrockTypes.OPTIONAL_PASSENGER_OF_BLOCKS.write(buffer, seated + ? new PassengerOfBlocks(new BlockPosition(-2, 64, 3), new Position3f(0F, 0.5F, 0F), 90F, 45F, PassengerOfBlockArguments_EmoteType.Riding) + : null); + BedrockTypes.STRING.write(buffer, deviceId); + buffer.writeIntLE(platform); + + final var input = spy(new PacketWrapperImpl(ClientboundPackets26_3.ADD_ENTITY, buffer, user)); + doNothing().when(input).send(any()); + final var profile = spy(new PacketWrapperImpl(ClientboundPackets26_3.PLAYER_INFO_UPDATE, null, user)); + doNothing().when(profile).send(any()); + try (var packets = mockStatic(PacketWrapper.class)) { + packets.when(() -> PacketWrapper.create(ClientboundPackets26_3.PLAYER_INFO_UPDATE, user)).thenReturn(profile); + packets.when(() -> PacketWrapper.create(ClientboundPackets26_3.SET_EQUIPMENT, user)).thenReturn(mock(PacketWrapper.class)); + handler.getValue().handle(input); + } + + assertFalse(buffer.isReadable()); + final GameProfile.Property[] expected = { + new GameProfile.Property("platform_online_id", "platform-chat"), + new GameProfile.Property("device_id", deviceId), + new GameProfile.Property("device_os", Integer.toString(platform)) + }; + assertArrayEquals(expected, profile.get(Types.PROFILE_PROPERTY_ARRAY, 0)); + verify(input).send(BedrockProtocol.class); + verify(profile).send(BedrockProtocol.class); + verify(player).sendInitialEntityData(); + } finally { + buffer.release(); + } + } + +} diff --git a/src/test/java/net/raphimc/viabedrock/protocol/packet/ResourcePackClientResponseTest.java b/src/test/java/net/raphimc/viabedrock/protocol/packet/ResourcePackClientResponseTest.java new file mode 100644 index 000000000..e00a7083a --- /dev/null +++ b/src/test/java/net/raphimc/viabedrock/protocol/packet/ResourcePackClientResponseTest.java @@ -0,0 +1,74 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2023-2026 RK_01/RaphiMC 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 net.raphimc.viabedrock.protocol.packet; + +import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; +import com.viaversion.viaversion.protocol.packet.PacketWrapperImpl; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.raphimc.viabedrock.protocol.ServerboundBedrockPackets; +import net.raphimc.viabedrock.protocol.data.enums.bedrock.generated.ResourcePackResponse; +import net.raphimc.viabedrock.protocol.types.BedrockTypes; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ResourcePackClientResponseTest { + + @Test + void downloadingUsesTheStringDiscriminatorAndVarintPackCount() throws Exception { + final String[] packIds = {"97e3f100-930a-4b47-b7ae-b8bbd38b1c19_1.0.0"}; + final PacketWrapper wrapper = new PacketWrapperImpl(ServerboundBedrockPackets.RESOURCE_PACK_CLIENT_RESPONSE, null, null); + ResourcePackClientResponse.writeDownloading(wrapper, packIds); + + final ByteBuf buffer = Unpooled.buffer(); + try { + wrapper.writeToBuffer(buffer); + assertEquals(ServerboundBedrockPackets.RESOURCE_PACK_CLIENT_RESPONSE.getId(), BedrockTypes.UNSIGNED_VAR_INT.read(buffer)); + assertEquals(1, BedrockTypes.UNSIGNED_VAR_INT.read(buffer)); + assertEquals("downloading", BedrockTypes.STRING.read(buffer)); + assertArrayEquals(packIds, BedrockTypes.STRING_ARRAY.read(buffer)); + assertEquals(0, buffer.readableBytes()); + } finally { + buffer.release(); + } + } + + @Test + void finishedResponsesContainTheStatusAndStringDiscriminator() throws Exception { + for (final ResourcePackResponse status : new ResourcePackResponse[]{ + ResourcePackResponse.DownloadingFinished, ResourcePackResponse.ResourcePackStackFinished}) { + final PacketWrapper wrapper = new PacketWrapperImpl(ServerboundBedrockPackets.RESOURCE_PACK_CLIENT_RESPONSE, null, null); + ResourcePackClientResponse.write(wrapper, status); + + final ByteBuf buffer = Unpooled.buffer(); + try { + wrapper.writeToBuffer(buffer); + assertEquals(ServerboundBedrockPackets.RESOURCE_PACK_CLIENT_RESPONSE.getId(), BedrockTypes.UNSIGNED_VAR_INT.read(buffer)); + assertEquals(status == ResourcePackResponse.DownloadingFinished ? 2 : 3, BedrockTypes.UNSIGNED_VAR_INT.read(buffer)); + assertEquals(status == ResourcePackResponse.DownloadingFinished + ? "downloadingfinished" : "resourcepackstackfinished", BedrockTypes.STRING.read(buffer)); + assertEquals(0, buffer.readableBytes()); + } finally { + buffer.release(); + } + } + } + +} diff --git a/src/test/java/net/raphimc/viabedrock/protocol/types/entitydata/EntityDataTypeTest.java b/src/test/java/net/raphimc/viabedrock/protocol/types/entitydata/EntityDataTypeTest.java new file mode 100644 index 000000000..fa10411d6 --- /dev/null +++ b/src/test/java/net/raphimc/viabedrock/protocol/types/entitydata/EntityDataTypeTest.java @@ -0,0 +1,55 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2023-2026 RK_01/RaphiMC 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 net.raphimc.viabedrock.protocol.types.entitydata; + +import io.netty.buffer.Unpooled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +final class EntityDataTypeTest { + + @Test + void readsBothDiscriminatorsAndLeavesTheFollowingFieldIntact() { + final var buffer = Unpooled.wrappedBuffer(new byte[]{18, 0, 0, 42, 99}); + try { + final var value = new EntityDataType().read(buffer); + assertEquals(18, value.id()); + assertEquals(EntityDataTypesBedrock.BYTE, value.dataType()); + assertEquals(Byte.valueOf((byte) 42), value.value()); + assertEquals(99, buffer.readUnsignedByte()); + assertFalse(buffer.isReadable()); + } finally { + buffer.release(); + } + } + + @Test + void rejectsUnknownAndMismatchedTypesBeforeReadingTheValue() { + for (final byte[] frame : new byte[][]{{18, 9, 9, 42}, {18, 1, 0, 42}}) { + final var buffer = Unpooled.wrappedBuffer(frame); + try { + assertThrows(IllegalStateException.class, () -> new EntityDataType().read(buffer)); + assertEquals(42, buffer.readUnsignedByte()); + } finally { + buffer.release(); + } + } + } + +} diff --git a/src/test/java/net/raphimc/viabedrock/protocol/types/model/FullContainerNameTypeTest.java b/src/test/java/net/raphimc/viabedrock/protocol/types/model/FullContainerNameTypeTest.java new file mode 100644 index 000000000..ab3f69e18 --- /dev/null +++ b/src/test/java/net/raphimc/viabedrock/protocol/types/model/FullContainerNameTypeTest.java @@ -0,0 +1,49 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2026 RK_01/RaphiMC 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. + */ +package net.raphimc.viabedrock.protocol.types.model; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import net.raphimc.viabedrock.protocol.data.enums.bedrock.generated.ContainerEnumName; +import net.raphimc.viabedrock.protocol.model.FullContainerName; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +class FullContainerNameTypeTest { + + @Test + void containerNamesUseProtocol2225SlotIds() { + final FullContainerNameType type = new FullContainerNameType(); + final Map ids = Map.of( + ContainerEnumName.InventoryContainer, 29, + ContainerEnumName.CursorContainer, 59, + ContainerEnumName.CreatedOutputContainer, 60, + ContainerEnumName.RecipeFoodContainer, 64, + ContainerEnumName.RecipeBlocksContainer, 65, + ContainerEnumName.RecipeFurnaceItemsContainer, 66); + + ids.forEach((name, id) -> { + final ByteBuf buffer = Unpooled.buffer(); + try { + type.write(buffer, new FullContainerName(name, null)); + assertArrayEquals(new byte[] {id.byteValue(), 0}, ByteBufUtil.getBytes(buffer)); + assertSame(name, ContainerEnumName.getByValue(id)); + } finally { + buffer.release(); + } + }); + } + +} diff --git a/src/test/java/net/raphimc/viabedrock/tool/generator/BedrockDataEnumGeneratorTest.java b/src/test/java/net/raphimc/viabedrock/tool/generator/BedrockDataEnumGeneratorTest.java new file mode 100644 index 000000000..de11610e3 --- /dev/null +++ b/src/test/java/net/raphimc/viabedrock/tool/generator/BedrockDataEnumGeneratorTest.java @@ -0,0 +1,73 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2023-2026 RK_01/RaphiMC 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 net.raphimc.viabedrock.tool.generator; + +import com.viaversion.viaversion.libs.gson.JsonObject; +import com.viaversion.viaversion.libs.gson.JsonParser; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +final class BedrockDataEnumGeneratorTest { + + @Test + void fillsOmittedPacketIdsInNumericOrderWithoutReplacingEnumAliases() { + final JsonObject packetEnum = packetEnum(); + final var missing = BedrockDataEnumGenerator.missingPacketIds(packetEnum, List.of( + packet("LastPacket", 357, 2225), packet("LoginPacket", 1, 2225), + packet("FirstPacket", 327, 2225))); + assertEquals(List.of("FirstPacket", "LastPacket"), List.copyOf(missing.keySet())); + assertEquals(Map.of("FirstPacket", 327, "LastPacket", 357), missing); + assertEquals(packetEnum(), packetEnum); + } + + @Test + void rejectsConflictingSchemaIdsAndEnumNames() { + assertThrows(IllegalStateException.class, () -> BedrockDataEnumGenerator.missingPacketIds( + packetEnum(), List.of(packet("OnePacket", 327, 2225), packet("TwoPacket", 327, 2225)))); + assertThrows(IllegalStateException.class, () -> BedrockDataEnumGenerator.missingPacketIds( + packetEnum(), List.of(packet("Login", 327, 2225)))); + } + + @Test + void rejectsMixedProtocolSnapshotsAndMissingExplicitValues() { + assertThrows(IllegalStateException.class, () -> BedrockDataEnumGenerator.missingPacketIds( + packetEnum(), List.of(packet("NewPacket", 327, 2193)))); + final JsonObject packetEnum = packetEnum(); + packetEnum.remove("x-enum-binary-value"); + assertThrows(IllegalStateException.class, () -> BedrockDataEnumGenerator.missingPacketIds(packetEnum, List.of())); + } + + private static JsonObject packetEnum() { + return JsonParser.parseString("{\"enum\":[\"Login\"],\"x-enum-binary-value\":[1],\"x-protocol-version\":2225}").getAsJsonObject(); + } + + private static JsonObject packet(final String name, final int id, final int protocol) { + final JsonObject packet = new JsonObject(); + packet.addProperty("title", name); + packet.addProperty("x-protocol-version", protocol); + final JsonObject properties = new JsonObject(); + properties.addProperty("[cereal:packet]", id); + packet.add("$metaProperties", properties); + return packet; + } + +} diff --git a/src/tool/java/net/raphimc/viabedrock/tool/generator/BedrockDataEnumGenerator.java b/src/tool/java/net/raphimc/viabedrock/tool/generator/BedrockDataEnumGenerator.java index 8a0954a15..c8cd4e7f9 100644 --- a/src/tool/java/net/raphimc/viabedrock/tool/generator/BedrockDataEnumGenerator.java +++ b/src/tool/java/net/raphimc/viabedrock/tool/generator/BedrockDataEnumGenerator.java @@ -30,7 +30,13 @@ import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; import java.util.stream.Stream; public final class BedrockDataEnumGenerator { @@ -50,8 +56,11 @@ public static void main(final String[] args) throws Throwable { throw new IllegalStateException("No enum definitions found in " + jsonDir); } + final List definitions = new ArrayList<>(); for (Path file : jsonFiles) { - final JsonObject jsonObject = gson.fromJson(Files.readString(file), JsonObject.class); + definitions.add(gson.fromJson(Files.readString(file), JsonObject.class)); + } + for (JsonObject jsonObject : definitions) { if (!jsonObject.has("enum")) { continue; @@ -135,6 +144,10 @@ public static void main(final String[] args) throws Throwable { genEnum.enumFields().add(new Field(name, null, null, value, new Javadoc())); } + if (enumName.equals("MinecraftPacketIds")) { + missingPacketIds(jsonObject, definitions).forEach((name, value) -> + genEnum.enumFields().add(new Field(name, null, null, Integer.toString(value), new Javadoc()))); + } codeGen.addType(genEnum); } @@ -142,6 +155,46 @@ public static void main(final String[] args) throws Throwable { System.out.println("Generated " + jsonFiles.size() + " enums from " + jsonDir); } + static Map missingPacketIds(final JsonObject packetEnum, final List definitions) { + final JsonArray names = packetEnum.getAsJsonArray("enum"); + final JsonArray values = packetEnum.getAsJsonArray("x-enum-binary-value"); + if (values == null || values.size() != names.size()) { + throw new IllegalStateException("Packet IDs require an explicit value for every enum entry"); + } + final Map namedIds = new LinkedHashMap<>(); + final Set enumIds = new HashSet<>(); + for (int i = 0; i < names.size(); i++) { + namedIds.put(names.get(i).getAsString(), values.get(i).getAsInt()); + enumIds.add(values.get(i).getAsInt()); + } + final Map schemaIds = new TreeMap<>(); + for (JsonObject definition : definitions) { + final JsonObject properties = definition.getAsJsonObject("$metaProperties"); + if (properties == null || !properties.has("[cereal:packet]")) { + continue; + } + if (!packetEnum.get("x-protocol-version").equals(definition.get("x-protocol-version"))) { + throw new IllegalStateException("Packet schemas and enum must target the same protocol"); + } + final String name = definition.get("title").getAsString(); + final int id = properties.get("[cereal:packet]").getAsInt(); + final String previous = schemaIds.putIfAbsent(id, name); + if (previous != null && !previous.equals(name)) { + throw new IllegalStateException("Conflicting packet schemas for ID " + id); + } + if (namedIds.containsKey(name) && namedIds.get(name) != id) { + throw new IllegalStateException("Conflicting packet ID for " + name); + } + } + final Map missing = new LinkedHashMap<>(); + schemaIds.forEach((id, name) -> { + if (!enumIds.contains(id)) { + missing.put(name, id); + } + }); + return missing; + } + private BedrockDataEnumGenerator() { }