Compare commits

...
1 Commits
Author SHA1 Message Date
Dan Mulloy aca303b228 Update to Minecraft 26.2 2026-06-20 10:39:34 -05:00
12 changed files with 117 additions and 20 deletions

No files matched your search

+1 -1
View File
@@ -12,7 +12,7 @@ plugins {
group = "net.dmulloy2"
description = "Provides access to the Minecraft protocol"
val mcVersion = "26.1"
val mcVersion = "26.2"
val isSnapshot = version.toString().endsWith("-SNAPSHOT")
val isJitPack = System.getenv("JITPACK")?.equals("true", ignoreCase = true) ?: false
val commitHash = System.getenv("COMMIT_SHA") ?: ""
@@ -506,7 +506,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x3B, "SetStructureBlock", "Struct");
public static final PacketType SET_TEST_BLOCK = new PacketType(PROTOCOL, SENDER, 0x3C, "SetTestBlock");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x3D, "SignUpdate", "UpdateSign", "CPacketUpdateSign");
public static final PacketType SPECTATE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x3E, "SpectateEntity");
public static final PacketType SPECTATE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x3E, "SpectatorAction", "SpectateEntity");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x3F, "Swing", "ArmAnimation", "CPacketAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x40, "TeleportToEntity", "Spectate", "CPacketSpectate");
public static final PacketType TEST_INSTANCE_BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x41, "TestInstanceBlockAction");
@@ -35,12 +35,12 @@ public class ProtocolLibrary {
/**
* The maximum version ProtocolLib has been tested with.
*/
public static final String MAXIMUM_MINECRAFT_VERSION = "26.1";
public static final String MAXIMUM_MINECRAFT_VERSION = "26.2";
/**
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.21.11) was released.
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (26.2) was released.
*/
public static final String MINECRAFT_LAST_RELEASE_DATE = "2026-03-24";
public static final String MINECRAFT_LAST_RELEASE_DATE = "2026-06-16";
private static Plugin plugin;
private static ProtocolConfig config;
@@ -412,8 +412,8 @@ public class PacketRegistry {
private static Class<?> searchForPacket(List<String> classNames) {
for (String name : classNames) {
try {
Class<?> clazz = MinecraftReflection.getMinecraftClass(name);
if (MinecraftReflection.getPacketClass().isAssignableFrom(clazz)
Class<?> clazz = resolvePacketClassName(name);
if (clazz != null && MinecraftReflection.getPacketClass().isAssignableFrom(clazz)
&& !Modifier.isAbstract(clazz.getModifiers())) {
return clazz;
}
@@ -424,6 +424,22 @@ public class PacketRegistry {
return null;
}
/**
* Resolve a single packet class name. Fully-qualified names (those already containing their
* package, e.g. {@code net.minecraft.network.protocol.game.XPacket}) are loaded directly, since
* {@link MinecraftReflection#getMinecraftClass(String)} would otherwise prepend the Minecraft
* package and fail to find them. Relative names continue to be resolved against the Minecraft
* package.
*/
private static Class<?> resolvePacketClassName(String name) {
Optional<Class<?>> direct = MinecraftReflection.getOptionalClass(name);
if (direct.isPresent()) {
return direct.get();
}
return MinecraftReflection.getMinecraftClass(name);
}
/**
* Retrieves the correct packet class from a given type.
*
@@ -100,6 +100,7 @@ public final class MinecraftProtocolVersion {
map.put(new MinecraftVersion(1, 21, 11), 774);
map.put(new MinecraftVersion(26, 1, 0), 775);
map.put(new MinecraftVersion(26, 2, 0), 776);
return map;
}
@@ -1118,6 +1118,15 @@ public final class MinecraftReflection {
return getOptionalNMS("network.chat.numbers.StyledFormat");
}
/**
* Retrieve the NMS TeamColor class, added in 26.2.
*
* @return The TeamColor class.
*/
public static Optional<Class<?>> getTeamColorClass() {
return getOptionalNMS("world.scores.TeamColor");
}
/**
* Retrieve the Gson class used by Minecraft.
*
@@ -1480,6 +1489,18 @@ public final class MinecraftReflection {
.orElseThrow(() -> new RuntimeException("Cannot find class " + className));
}
/**
* Attempt to load a class by its fully-qualified canonical name, without prepending the
* Minecraft package. Unlike {@link #getMinecraftClass(String)}, this can resolve names that
* already include their package (e.g. {@code net.minecraft.network.protocol.game.XPacket}).
*
* @param className - the fully-qualified class name.
* @return Optional that may contain the class.
*/
public static Optional<Class<?>> getOptionalClass(String className) {
return getClassSource().loadClass(className);
}
/**
* Retrieve the class object of a specific CraftBukkit class.
*
@@ -1684,7 +1705,7 @@ public final class MinecraftReflection {
}
public static Class<?> getEntityTypes() {
return getMinecraftClass("world.entity.EntityTypes", "world.entity.EntityType", "EntityTypes");
return getMinecraftClass("world.entity.EntityType", "world.entity.EntityTypes", "EntityTypes");
}
public static Class<?> getParticleParam() {
@@ -36,6 +36,11 @@ import org.bukkit.Server;
* @author Kristian
*/
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
/**
* Version 26.2 - chaos cubed
*/
public static final MinecraftVersion v26_2 = new MinecraftVersion("26.2");
/**
* Version 26.1 - tiny takeover
*/
@@ -189,7 +194,7 @@ public final class MinecraftVersion implements Comparable<MinecraftVersion>, Ser
/**
* The latest release version of minecraft.
*/
public static final MinecraftVersion LATEST = v26_1;
public static final MinecraftVersion LATEST = v26_2;
// used when serializing
private static final long serialVersionUID = -8695133558996459770L;
@@ -70,6 +70,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldType;
@@ -873,6 +874,13 @@ public class BukkitConverters {
return ignoreNull(new EquivalentConverter<EntityType>() {
@Override
public Object getGeneric(EntityType specific) {
if (MinecraftVersion.v26_2.atOrAbove()) {
// EntityType.byString was removed in 26.2, resolve through the registry instead
WrappedRegistry registry = WrappedRegistry.getRegistry(MinecraftReflection.getEntityTypes());
NamespacedKey key = specific.getKey();
return registry.get(new MinecraftKey(key.getNamespace(), key.getKey()));
}
if (entityTypeFromName == null) {
Class<?> entityTypesClass = MinecraftReflection.getEntityTypes();
entityTypeFromName = Accessors.getMethodAccessor(
@@ -3,6 +3,8 @@ package com.comphenix.protocol.wrappers;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
@@ -90,12 +92,26 @@ public class WrappedTeamParameters extends AbstractWrapper {
@NotNull
public EnumWrappers.ChatFormatting getColor() {
if (MinecraftVersion.v26_2.atOrAbove()) {
Optional<?> optional = modifier.<Optional<?>>withType(Optional.class).read(0);
Object teamColor = optional != null ? optional.orElse(null) : null;
if (teamColor == null) {
return EnumWrappers.ChatFormatting.RESET;
}
return EnumWrappers.ChatFormatting.valueOf(((Enum<?>) teamColor).name());
}
return modifier
.withType(EnumWrappers.getChatFormattingClass(), EnumWrappers.getChatFormattingConverter())
.read(0);
}
public int getOptions() {
if (MinecraftVersion.v26_2.atOrAbove()) {
return (byte) modifier.withType(byte.class).read(0);
}
return (int) modifier.withType(int.class).read(0);
}
@@ -210,9 +226,27 @@ public class WrappedTeamParameters extends AbstractWrapper {
wrapped.modifier.withType(String.class).writeSafely(1, collisionRule.toString());
}
wrapped.modifier.withType(EnumWrappers.getChatFormattingClass()).write(0, EnumWrappers.getChatFormattingConverter().getGeneric(color));
wrapped.modifier.withType(int.class).write(0, options);
if (MinecraftVersion.v26_2.atOrAbove()) {
wrapped.modifier.withType(Optional.class).write(0, Optional.ofNullable(toNmsTeamColor(color)));
wrapped.modifier.withType(byte.class).write(0, (byte) options);
} else {
wrapped.modifier.withType(EnumWrappers.getChatFormattingClass()).write(0, EnumWrappers.getChatFormattingConverter().getGeneric(color));
wrapped.modifier.withType(int.class).write(0, options);
}
return wrapped;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static Object toNmsTeamColor(EnumWrappers.ChatFormatting color) {
Class<?> teamColorClass = MinecraftReflection.getTeamColorClass()
.orElseThrow(() -> new IllegalStateException("TeamColor class doesn't exist on this server version"));
try {
return Enum.valueOf((Class) teamColorClass, color.name());
} catch (IllegalArgumentException ex) {
// formatting-only values (e.g. RESET) don't map to a team color
return null;
}
}
}
}
@@ -79,6 +79,15 @@ public class BukkitInitialization {
private boolean initialized;
private boolean packaged;
private static ServerLevel mockServerLevel;
/**
* @return the mocked NMS server level set up during initialization, usable as a Level for constructing test entities
*/
public static ServerLevel getMockServerLevel() {
return mockServerLevel;
}
private BukkitInitialization() {
System.out.println("Created new BukkitInitialization on " + Thread.currentThread().getName());
}
@@ -246,6 +255,7 @@ public class BukkitInitialization {
CraftWorld world = mock(CraftWorld.class);
when(world.getHandle()).thenReturn(nmsWorld);
mockServerLevel = nmsWorld;
List<World> worlds = Collections.singletonList(world);
when(mockedServer.getWorlds()).thenReturn(worlds);
@@ -46,7 +46,7 @@ public class WrappedDataWatcherTest {
public static void prepare() {
BukkitInitialization.initializeAll();
ThrownEgg nmsEgg = new ThrownEgg(null, 0, 0, 0, net.minecraft.world.item.ItemStack.EMPTY);
ThrownEgg nmsEgg = new ThrownEgg(BukkitInitialization.getMockServerLevel(), 0, 0, 0, net.minecraft.world.item.ItemStack.EMPTY);
mockEntity = new CraftEgg(null, nmsEgg);
}
@@ -5,14 +5,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.Optional;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.EnumWrappers.TeamCollisionRule;
import com.comphenix.protocol.wrappers.EnumWrappers.TeamVisibility;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
import net.minecraft.world.scores.Team;
import net.minecraft.world.scores.TeamColor;
public class WrappedTeamParametersTest {
@BeforeAll
@@ -47,12 +49,12 @@ public class WrappedTeamParametersTest {
assertEquals(1, wrapped.getOptions());
ClientboundSetPlayerTeamPacket.Parameters handle = (ClientboundSetPlayerTeamPacket.Parameters) wrapped.getHandle();
assertEquals(handle.getDisplayName(), displayName);
assertEquals(handle.getPlayerPrefix(), prefix);
assertEquals(handle.getPlayerSuffix(), suffix);
assertEquals(handle.getNametagVisibility(), Team.Visibility.ALWAYS);
assertEquals(handle.getCollisionRule(), Team.CollisionRule.NEVER);
assertEquals(handle.getColor(), ChatFormatting.RED);
assertEquals(handle.getOptions(), 1);
assertEquals(handle.displayName(), displayName);
assertEquals(handle.playerPrefix(), prefix);
assertEquals(handle.playerSuffix(), suffix);
assertEquals(handle.nameTagVisibility(), Team.Visibility.ALWAYS);
assertEquals(handle.collisionRule(), Team.CollisionRule.NEVER);
assertEquals(handle.color(), Optional.of(TeamColor.RED));
assertEquals(handle.options(), 1);
}
}