Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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") ?: ""
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/comphenix/protocol/PacketType.java
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ public static class Client extends PacketTypeEnum {
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");
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/comphenix/protocol/ProtocolLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,8 @@ public static Set<PacketType> getClientPacketTypes() {
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;
}
Expand All @@ -424,6 +424,22 @@ private static Class<?> searchForPacket(List<String> classNames) {
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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ private static NavigableMap<MinecraftVersion, Integer> createLookup() {
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,15 @@ public static Optional<Class<?>> getStyledFormatClass() {
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.
*
Expand Down Expand Up @@ -1480,6 +1489,18 @@ private static Class<?> getClass(String className) {
.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.
*
Expand Down Expand Up @@ -1684,7 +1705,7 @@ public static Class<?> getResourceKey() {
}

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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
* @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
*/
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
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;
Expand Down Expand Up @@ -873,6 +874,13 @@ public static EquivalentConverter<EntityType> getEntityTypeConverter() {
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
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;
Expand Down Expand Up @@ -90,12 +92,26 @@ public TeamCollisionRule getCollisionRule() {

@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);
}

Expand Down Expand Up @@ -210,9 +226,27 @@ public WrappedTeamParameters build() {
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;
}
}
}
}
10 changes: 10 additions & 0 deletions src/test/java/com/comphenix/protocol/BukkitInitialization.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down Expand Up @@ -246,6 +255,7 @@ private void initialize() {

CraftWorld world = mock(CraftWorld.class);
when(world.getHandle()).thenReturn(nmsWorld);
mockServerLevel = nmsWorld;

List<World> worlds = Collections.singletonList(world);
when(mockedServer.getWorlds()).thenReturn(worlds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
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
Expand Down Expand Up @@ -47,12 +49,12 @@ public void testTeamParameters() {
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);
}
}
Loading