Compare commits

...
12 Commits
Author SHA1 Message Date
Dan Mulloy 4649daef82 fix: fail gracefully for unknown protocol 2024-06-14 13:59:53 -05:00
Dan Mulloy 70d47ce622 wip: mojang mappings 2024-06-13 20:17:15 -05:00
Dan Mulloy 46d40d752e fix: EnumProtocol Mojang mapping 2024-06-13 20:16:52 -05:00
Dan Mulloy a006b70c6f fix: JVM error when instantiating certain types 2024-06-13 19:49:24 -05:00
Dan Mulloy e726f6eb0d 1.21 2024-06-13 18:01:40 -05:00
Dan Mulloy 389233f599 Don't wrap attribute in holder < 1.20.5
Addresses #2998
2024-06-11 21:08:02 -05:00
Dan Mulloy 4051caca0b Another one
Addresses #2998
2024-06-10 12:07:29 -05:00
Dan Mulloy 914bc9aa7f Fix error on 1.20.4
Fixes #2998
2024-06-10 10:13:27 -05:00
Dan Mulloy 8272ecb33d Fix some more Mojang mappings 2024-06-09 14:53:38 -05:00
Dan Mulloy d13ce24507 Stop caching constructor parameters
Addresses #2990
2024-06-08 14:09:23 -05:00
Dan Mulloy b6a7d5f91d Fix some more Mojang mappings 2024-06-08 13:45:17 -05:00
Dan Mulloy f6b3af4221 Add helper methods for data watchers, fix wrapping
Addresses #2987
2024-06-08 12:04:44 -05:00
55 changed files with 1529 additions and 805 deletions

No files matched your search

+11 -3
View File
@@ -11,7 +11,7 @@ description = 'Provides access to the Minecraft protocol'
def isSnapshot = version.endsWith('-SNAPSHOT')
repositories {
// mavenLocal() // can speed up build, but may fail in CI
mavenLocal() // can speed up build, but may fail in CI
mavenCentral()
maven {
@@ -35,7 +35,7 @@ repositories {
dependencies {
implementation 'net.bytebuddy:byte-buddy:1.14.14'
compileOnly 'org.spigotmc:spigot-api:1.20.6-R0.1-SNAPSHOT'
compileOnly 'org.spigotmc:spigot:1.20.6-R0.1-SNAPSHOT'
compileOnly 'org.spigotmc:spigot:1.21-R0.1-SNAPSHOT:remapped-mojang'
compileOnly 'io.netty:netty-all:4.0.23.Final'
compileOnly 'net.kyori:adventure-text-serializer-gson:4.14.0'
compileOnly 'com.googlecode.json-simple:json-simple:1.1.1'
@@ -46,7 +46,7 @@ dependencies {
testImplementation 'org.mockito:mockito-core:5.6.0'
testImplementation 'io.netty:netty-common:4.1.97.Final'
testImplementation 'io.netty:netty-transport:4.1.97.Final'
testImplementation 'org.spigotmc:spigot:1.20.6-R0.1-SNAPSHOT'
testImplementation 'org.spigotmc:spigot:1.21-R0.1-SNAPSHOT:remapped-mojang'
testImplementation 'net.kyori:adventure-text-serializer-gson:4.14.0'
testImplementation 'net.kyori:adventure-text-serializer-plain:4.14.0'
}
@@ -59,6 +59,14 @@ java {
withSourcesJar()
}
jar {
manifest {
attributes(
'paperweight-mappings-namespace': 'mojang'
)
}
}
shadowJar {
dependencies {
include(dependency('net.bytebuddy:byte-buddy:.*'))
@@ -14,18 +14,18 @@ import java.util.Map;
import java.util.UUID;
import java.util.function.BiConsumer;
import org.apache.commons.lang.WordUtils;
import org.bukkit.Bukkit;
import com.comphenix.protocol.PacketTypeLookup.ClassLookup;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.scheduler.UniversalRunnable;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Preconditions;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Iterables;
import org.apache.commons.lang.WordUtils;
import org.bukkit.Bukkit;
/**
* Represents the type of a packet in a specific protocol.
* <p>
@@ -229,6 +229,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x77, "UpdateRecipes", "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x78, "UpdateTags", "Tags");
public static final PacketType PROJECTILE_POWER = new PacketType(PROTOCOL, SENDER, 0x79, "ProjectilePower");
public static final PacketType REPORT_DETAILS = new PacketType(PROTOCOL, SENDER, 0x7A, "CustomReportDetails");
public static final PacketType SERVER_LINKS = new PacketType(PROTOCOL, SENDER, 0x7B, "ServerLinks");
// ---- Removed in 1.9
@@ -477,8 +479,12 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x35, "SignUpdate", "UpdateSign", "CPacketUpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x36, "Swing", "ArmAnimation", "CPacketAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x37, "TeleportToEntity", "Spectate", "CPacketSpectate");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x38, "UseItemOn", "UseItem", "CPacketPlayerTryUseItemOnBlock");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x39, "BlockPlace", "CPacketPlayerTryUseItem");
public static final PacketType USE_ITEM_ON = new PacketType(PROTOCOL, SENDER, 0x38, "UseItemOn");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x39, "UseItem");
// public static final PacketType USE_ITEM_ON = new PacketType(PROTOCOL, SENDER, 0x38, "UseItemOn", "BlockPlace", "CPacketPlayerTryUseItemOnBlock");
// public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x39, "UseItem", "CPacketPlayerTryUseItem");
/**
* @deprecated Removed in 1.17
@@ -498,6 +504,12 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@Deprecated
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 253, "ChatPreview");
/**
* @deprecated Renamed to USE_ITEM_ON
*/
@Deprecated
public static final PacketType BLOCK_PLACE = USE_ITEM_ON.clone();
private static final Client INSTANCE = new Client();
// Prevent accidental construction
@@ -678,6 +690,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType UPDATE_ENABLED_FEATURES = new PacketType(PROTOCOL, SENDER, 0x0C, "UpdateEnabledFeatures");
public static final PacketType UPDATE_TAGS = new PacketType(PROTOCOL, SENDER, 0x0D, "UpdateTags");
public static final PacketType SELECT_KNOWN_PACKS = new PacketType(PROTOCOL, SENDER, 0x0E, "ClientboundSelectKnownPacks");
public static final PacketType REPORT_DETAILS = new PacketType(PROTOCOL, SENDER, 0x0F, "CustomReportDetails");
public static final PacketType SERVER_LINKS = new PacketType(PROTOCOL, SENDER, 0x10, "ServerLinks");
/**
* @deprecated Removed in 1.20.4: replaced with new packets for removing and sending resource packs
@@ -731,12 +745,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
STATUS("Status", "status"),
LOGIN("Login", "login"),
CONFIGURATION("Configuration", "configuration"),
TRANSFER("Transfer", "transfer"), // TODO are these the right names?
/**
* Only for packets removed in Minecraft 1.7.2
*/
LEGACY("", "");
UNKNOWN("", "");
private final String packetName;
private final String mojangName;
@@ -752,14 +761,17 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @return The corresponding protocol.
*/
public static Protocol fromVanilla(Enum<?> vanilla) {
if (vanilla == null) {
return UNKNOWN;
}
switch (vanilla.name()) {
case "HANDSHAKING": return HANDSHAKING;
case "PLAY": return PLAY;
case "STATUS": return STATUS;
case "LOGIN": return LOGIN;
case "CONFIGURATION": return CONFIGURATION;
default:
throw new IllegalArgumentException("Unrecognized vanilla enum " + vanilla);
default: return UNKNOWN;
}
}
@@ -31,6 +31,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import javax.annotation.Nullable;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.StructureCache;
@@ -58,10 +59,10 @@ import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.Converters;
import com.comphenix.protocol.wrappers.WrappedStreamCodec;
import com.google.common.collect.Sets;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
import javax.annotation.Nullable;
/**
* Represents a Minecraft packet indirectly.
@@ -80,7 +81,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
// Used to clone packets
private static final AggregateCloner DEEP_CLONER = AggregateCloner
.newBuilder()
.instanceProvider(StructureCache::newPacket)
.instanceProvider(StructureCache::newInstance)
.andThen(BukkitCloner.class)
.andThen(ImmutableDetector.class)
.andThen(JavaOptionalCloner.class)
@@ -91,7 +92,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
private static final AggregateCloner SHALLOW_CLONER = AggregateCloner
.newBuilder()
.instanceProvider(StructureCache::newPacket)
.instanceProvider(StructureCache::newInstance)
.andThen(param -> {
if (param == null)
throw new IllegalArgumentException("Cannot be NULL.");
@@ -152,7 +152,6 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return lastSeen;
}
@Override
public Location getRespawnLocation() {
return null;
}
@@ -241,11 +240,21 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return uuid;
}
/**
* @return
*/
@NotNull
@Override
public @NotNull PlayerProfile getPlayerProfile() {
public PlayerProfile getPlayerProfile() {
return null;
}
/*
public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() {
return null;
}
*/
@Override
public String getName() {
return name;
@@ -288,6 +297,11 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return online;
}
// @Override
public boolean isConnected() {
return false;
}
@Override
public boolean isWhitelisted() {
return whitelisted;
@@ -17,22 +17,28 @@
package com.comphenix.protocol.injector;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.PublicKey;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Supplier;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.packet.KnownPacketData;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.PacketCreator;
import com.comphenix.protocol.reflect.instances.InstanceCreator;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
@@ -41,9 +47,10 @@ import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.ZeroBuffer;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedStreamCodec;
import com.google.common.base.Preconditions;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy.Default;
import net.bytebuddy.implementation.FixedValue;
@@ -57,38 +64,96 @@ import net.bytebuddy.matcher.ElementMatchers;
public class StructureCache {
// Structure modifiers
private static final Map<Class<?>, Supplier<Object>> PACKET_INSTANCE_CREATORS = new ConcurrentHashMap<>();
private static final Map<Class<?>, Optional<Supplier<Object>>> CACHED_INSTANCE_CREATORS = new ConcurrentHashMap<>();
private static final Map<PacketType, StructureModifier<Object>> STRUCTURE_MODIFIER_CACHE = new ConcurrentHashMap<>();
// packet data serializer which always returns an empty nbt tag compound
private static final Object TRICK_INIT_LOCK = new Object();
private static boolean TRICK_TRIED = false;
private static Supplier<Object> TRICKED_DATA_SERIALIZER_BASE;
private static Function<ByteBuf, Object> TRICKED_DATA_SERIALIZER_BASE;
private static Supplier<Object> TRICKED_DATA_SERIALIZER_JSON;
/**
* @deprecated Renamed to {@link #newInstance(Class)}.
*/
@Deprecated
public static Object newPacket(Class<?> packetClass) {
Supplier<Object> packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, packetClassKey -> {
try {
PacketCreator creator = PacketCreator.forPacket(packetClassKey);
if (creator.get() != null) {
return creator;
}
} catch (Exception ignored) {
}
return newInstance(packetClass);
}
WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(packetClassKey);
public static boolean canCreateInstance(Class<?> clazz) {
Optional<Supplier<Object>> creator = CACHED_INSTANCE_CREATORS.computeIfAbsent(clazz, x ->
Optional.ofNullable(determineBestCreator(clazz)));
return creator.isPresent();
}
// use the new stream codec for versions above 1.20.5 if possible
if (streamCodec != null && tryInitTrickDataSerializer()) {
public static Object newInstance(Class<?> clazz) {
Optional<Supplier<Object>> creator = CACHED_INSTANCE_CREATORS.computeIfAbsent(clazz, x ->
Optional.ofNullable(determineBestCreator(clazz)));
if (!creator.isPresent()) {
throw new IllegalArgumentException("Cannot create instance of " + clazz);
}
return creator.get().get();
}
static Supplier<Object> determineBestCreator(Class<?> clazz) {
// certain packets are singletons which can't really be created
if (MinecraftReflection.isPacketClass(clazz)) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
List<Field> singletons = fuzzy.getFieldList(FuzzyFieldContract.newBuilder()
.typeExact(clazz)
.requireModifier(Modifier.STATIC)
.requireModifier(Modifier.PUBLIC)
.build());
if (singletons.size() == 1) {
FieldAccessor accessor = Accessors.getFieldAccessor(singletons.get(0));
try {
// first try with the base accessor
Object serializer = TRICKED_DATA_SERIALIZER_BASE.get();
streamCodec.decode(serializer); // throwaway instance, for testing
// method is working
return () -> streamCodec.decode(serializer);
accessor.get(null);
return () -> accessor.get(null);
} catch (Exception ignored) {
}
}
}
try {
InstanceCreator creator = InstanceCreator.forClass(clazz);
if (creator.get() != null) {
return creator;
}
} catch (Exception ignored) {
}
WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(clazz);
// use the new stream codec for versions above 1.20.5 if possible
if (streamCodec != null && tryInitTrickDataSerializer()) {
try {
// first try with the base accessor
Object serializer = TRICKED_DATA_SERIALIZER_BASE.apply(new ZeroBuffer());
streamCodec.decode(serializer); // throwaway instance, for testing
// method is working
return () -> streamCodec.decode(serializer);
} catch (Exception ex) {
try {
byte[] data;
if (clazz.equals(PacketType.Play.Server.MAP_CHUNK.getPacketClass())) {
data = KnownPacketData.MAP_CHUNK;
} else if (clazz.equals(PacketType.Play.Server.SCOREBOARD_OBJECTIVE.getPacketClass())) {
data = KnownPacketData.SCOREBOARD_OBJECTIVE;
} else {
throw ex;
}
Object serializer = TRICKED_DATA_SERIALIZER_BASE.apply(Unpooled.copiedBuffer(data));
streamCodec.decode(serializer);
return () -> {
((ByteBuf) serializer).readerIndex(0);
return streamCodec.decode(serializer);
};
} catch (Exception ignored1) {
try {
// try with the json accessor
Object serializer = TRICKED_DATA_SERIALIZER_JSON.get();
@@ -96,51 +161,50 @@ public class StructureCache {
// method is working
return () -> streamCodec.decode(serializer);
} catch (Exception ignored1) {
} catch (Exception ignored2) {
// shrug, fall back to default behaviour
}
}
}
}
// prefer construction via PacketDataSerializer constructor on 1.17 and above
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
ConstructorAccessor serializerAccessor = Accessors.getConstructorAccessorOrNull(
packetClassKey,
MinecraftReflection.getPacketDataSerializerClass());
if (serializerAccessor != null) {
// check if the method is possible
if (tryInitTrickDataSerializer()) {
// prefer construction via PacketDataSerializer constructor on 1.17 and above
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
ConstructorAccessor serializerAccessor = Accessors.getConstructorAccessorOrNull(
clazz,
MinecraftReflection.getPacketDataSerializerClass());
if (serializerAccessor != null) {
// check if the method is possible
if (tryInitTrickDataSerializer()) {
try {
// first try with the base accessor
Object serializer = TRICKED_DATA_SERIALIZER_BASE.apply(new ZeroBuffer());
serializerAccessor.invoke(serializer); // throwaway instance, for testing
// method is working
return () -> serializerAccessor.invoke(serializer);
} catch (Exception ignored) {
try {
// first try with the base accessor
Object serializer = TRICKED_DATA_SERIALIZER_BASE.get();
// try with the json accessor
Object serializer = TRICKED_DATA_SERIALIZER_JSON.get();
serializerAccessor.invoke(serializer); // throwaway instance, for testing
// method is working
return () -> serializerAccessor.invoke(serializer);
} catch (Exception ignored) {
try {
// try with the json accessor
Object serializer = TRICKED_DATA_SERIALIZER_JSON.get();
serializerAccessor.invoke(serializer); // throwaway instance, for testing
// method is working
return () -> serializerAccessor.invoke(serializer);
} catch (Exception ignored1) {
// shrug, fall back to default behaviour
}
} catch (Exception ignored1) {
// shrug, fall back to default behaviour
}
}
}
}
}
// try via DefaultInstances as fallback
return () -> {
Object packetInstance = DefaultInstances.DEFAULT.create(packetClassKey);
Objects.requireNonNull(packetInstance, "Unable to create packet instance for class " + packetClassKey + " - " + tryInitTrickDataSerializer() + " - " + streamCodec);
return packetInstance;
};
});
return packetConstructor.get();
Object instance = DefaultInstances.DEFAULT.create(clazz);
if (instance == null) {
return null;
}
return () -> DefaultInstances.DEFAULT.create(clazz);
}
/**
@@ -196,7 +260,7 @@ public class StructureCache {
*/
public static Object newNullDataSerializer() {
tryInitTrickDataSerializer();
return TRICKED_DATA_SERIALIZER_BASE.get();
return TRICKED_DATA_SERIALIZER_BASE.apply(new ZeroBuffer());
}
static void initTrickDataSerializer() {
@@ -226,10 +290,10 @@ public class StructureCache {
.parameterDerivedOf(ByteBuf.class)
.parameterDerivedOf(MinecraftReflection.getRegistryAccessClass())
.build()));
TRICKED_DATA_SERIALIZER_BASE = () -> accessor.invoke(new ZeroBuffer(), MinecraftRegistryAccess.get());
TRICKED_DATA_SERIALIZER_BASE = (buf) -> accessor.invoke(buf, MinecraftRegistryAccess.get());
} else {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
TRICKED_DATA_SERIALIZER_BASE = () -> accessor.invoke(new ZeroBuffer());
TRICKED_DATA_SERIALIZER_BASE = accessor::invoke;
}
//xtended builder which intercepts the read string method as well
@@ -11,9 +11,6 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.ProtocolLibrary;
@@ -46,6 +43,8 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoop;
import io.netty.util.AttributeKey;
import org.bukkit.Server;
import org.bukkit.entity.Player;
public class NettyChannelInjector implements Injector {
@@ -583,8 +582,12 @@ public class NettyChannelInjector implements Injector {
}
PacketType.Protocol protocol = this.getCurrentProtocol(PacketType.Sender.SERVER);
if (protocol == Protocol.UNKNOWN) {
ProtocolLogger.debug("skipping unknown protocol for {0}", packet.getClass());
return action;
}
PacketType packetType = PacketRegistry.getPacketType(protocol, packet.getClass());
if (packetType == null) {
ProtocolLogger.debug("skipping unknown outbound packet type for {0}", packet.getClass());
return action;
@@ -0,0 +1,13 @@
package com.comphenix.protocol.injector.packet;
public final class KnownPacketData {
public static final byte[] SCOREBOARD_OBJECTIVE = {
4, 84, 101, 115, 116, 1
};
public static final byte[] MAP_CHUNK = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
};
}
@@ -370,7 +370,7 @@ public class PacketRegistry {
Class<?> idCodecEntryClass = MinecraftReflection.getMinecraftClass("network.codec.IdDispatchCodec$Entry", "network.codec.IdDispatchCodec$b");
Class<?> protocolDirectionClass = MinecraftReflection.getPacketFlowClass();
Function<?, ?> emptyFunction = input -> null;
Function<?, ?> emptyFunction = input -> input;
FuzzyReflection protocolInfoReflection = FuzzyReflection.fromClass(protocolInfoClass);
@@ -17,15 +17,6 @@
package com.comphenix.protocol.reflect;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.instances.BannedGenerator;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.InstanceProvider;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
@@ -43,6 +34,17 @@ import java.util.Optional;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.instances.BannedGenerator;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.InstanceProvider;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
/**
* Provides list-oriented access to the fields of a Minecraft packet.
* <p>
@@ -142,8 +144,9 @@ public class StructureModifier<T> {
for (FieldAccessor accessor : fields) {
Field field = accessor.getField();
if (!field.getType().isPrimitive() && !Modifier.isFinal(field.getModifiers())) {
if (DEFAULT_GENERATOR.hasDefault(field.getType())) {
if (!field.getType().isPrimitive()) {// && !Modifier.isFinal(field.getModifiers())) {
// if (DEFAULT_GENERATOR.hasDefault(field.getType())) {
if (StructureCache.canCreateInstance(field.getType())) {
requireDefaults.put(accessor, currentFieldIndex);
}
}
@@ -395,13 +398,12 @@ public class StructureModifier<T> {
// They must be null or messages will be blank
Field field = accessor.getField();
if (field.getType().getCanonicalName().equals("net.md_5.bungee.api.chat.BaseComponent[]")) {
accessor.set(this.target, null);
accessor.set(target, null);
continue;
}
// get the default value and write the field
Object defaultValue = DEFAULT_GENERATOR.getDefault(field.getType());
accessor.set(this.target, defaultValue);
Object value = StructureCache.newInstance(field.getType());
accessor.set(target, value);
}
return this;
@@ -82,6 +82,8 @@ public class BukkitCloner implements Cloner {
fromWrapper(MinecraftReflection::getIChatBaseComponentClass, WrappedChatComponent::fromHandle);
fromWrapper(WrappedVillagerData::getNmsClass, WrappedVillagerData::fromHandle);
fromConverter(MinecraftReflection::getSectionPositionClass, BukkitConverters.getSectionPositionConverter());
fromManual(MinecraftReflection::getAttributeSnapshotClass, source ->
WrappedAttribute.fromHandle(source).shallowClone().getHandle());
try {
fromManual(ComponentConverter::getBaseComponentArrayClass, source ->
@@ -62,12 +62,12 @@ public class ImmutableDetector implements Cloner {
add(MinecraftReflection::getDataWatcherSerializerClass);
add(MinecraftReflection::getBlockClass);
add(MinecraftReflection::getItemClass);
add("sounds.SoundEffect", "sounds.SoundEvents", "SoundEffect");
add(MinecraftReflection::getSoundEffectClass);
if (MinecraftVersion.AQUATIC_UPDATE.atOrAbove()) {
add(MinecraftReflection::getFluidTypeClass);
add(MinecraftReflection::getParticleTypeClass);
add("core.particles.Particle","core.particles.ParticleType", "Particle");
add(MinecraftReflection::getParticleClass);
}
if (MinecraftVersion.VILLAGE_UPDATE.atOrAbove()) {
@@ -3,35 +3,44 @@ package com.comphenix.protocol.reflect.instances;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.function.Supplier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
public final class PacketCreator implements Supplier<Object> {
public final class InstanceCreator implements Supplier<Object> {
private static Map<Class<?>, Object> BANNED_PARAMETERS = new WeakHashMap<>();
static {
try {
BANNED_PARAMETERS.put(ByteBuffer.class, true);
BANNED_PARAMETERS.put(FloatBuffer.class, true);
} catch (Throwable ignored) {
}
}
private ConstructorAccessor constructor = null;
private MethodAccessor factoryMethod = null;
private Object[] params = null;
private Class<?>[] paramTypes = null;
private boolean failed = false;
private final Class<?> type;
private PacketCreator(Class<?> type) {
private InstanceCreator(Class<?> type) {
this.type = type;
}
public static PacketCreator forPacket(Class<?> type) {
public static InstanceCreator forClass(Class<?> type) {
if (type == null) {
throw new IllegalArgumentException("Type cannot be null.");
}
if (!MinecraftReflection.getPacketClass().isAssignableFrom(type)) {
throw new IllegalArgumentException("Type must be a subclass of Packet.");
}
return new PacketCreator(type);
return new InstanceCreator(type);
}
private Object createInstance(Class<?> clazz) {
@@ -42,8 +51,27 @@ public final class PacketCreator implements Supplier<Object> {
}
}
private Object[] createParams(Class<?>[] paramTypes) {
Object[] params = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
params[i] = createInstance(paramTypes[i]);
}
return params;
}
private boolean containsBannedParameter(Class<?>[] paramTypes) {
for (Class<?> paramType : paramTypes) {
if (BANNED_PARAMETERS.containsKey(paramType)) {
return true;
}
}
return false;
}
@Override
public Object get() {
Object[] params = paramTypes != null ? createParams(paramTypes) : null;
if (constructor != null) {
return constructor.invoke(params);
}
@@ -59,22 +87,24 @@ public final class PacketCreator implements Supplier<Object> {
Object result = null;
int minCount = Integer.MAX_VALUE;
for (Constructor<?> testCtor : type.getConstructors()) {
for (Constructor<?> testCtor : type.getDeclaredConstructors()) {
Class<?>[] paramTypes = testCtor.getParameterTypes();
if (paramTypes.length > minCount) {
continue;
}
Object[] testParams = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
testParams[i] = createInstance(paramTypes[i]);
if (containsBannedParameter(paramTypes)) {
continue;
}
Object[] testParams = createParams(paramTypes);
try {
result = testCtor.newInstance(testParams);
ConstructorAccessor testAccessor = Accessors.getConstructorAccessor(testCtor);
result = testAccessor.invoke(testParams);
minCount = paramTypes.length;
this.constructor = Accessors.getConstructorAccessor(testCtor);
this.params = testParams;
this.constructor = testAccessor;
this.paramTypes = paramTypes;
} catch (Exception ignored) {
}
}
@@ -100,16 +130,18 @@ public final class PacketCreator implements Supplier<Object> {
continue;
}
Object[] testParams = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
testParams[i] = createInstance(paramTypes[i]);
if (containsBannedParameter(paramTypes)) {
continue;
}
Object[] testParams = createParams(paramTypes);
try {
result = testMethod.invoke(null, testParams);
MethodAccessor testAccessor = Accessors.getMethodAccessor(testMethod);
result = testAccessor.invoke(null, testParams);
minCount = paramTypes.length;
this.factoryMethod = Accessors.getMethodAccessor(testMethod);
this.params = testParams;
this.factoryMethod = testAccessor;
this.paramTypes = paramTypes;
} catch (Exception ignored) {
}
}
@@ -64,11 +64,16 @@ final class CachedPackage {
* @param clazz - type of class.
*/
public void setPackageClass(String className, Class<?> clazz) {
if (clazz != null) {
this.cache.put(className, Optional.of(clazz));
} else {
this.cache.remove(className);
Optional<Class<?>> previous = cache.get(className);
if (previous != null && previous.isPresent()) {
throw new IllegalStateException("Tried to redefine class " + className);
}
cache.put(className, Optional.ofNullable(clazz));
}
public void removePackageClass(String className) {
cache.remove(className);
}
private Optional<Class<?>> resolveClass(String className) {
@@ -88,8 +88,11 @@ public final class MinecraftProtocolVersion {
map.put(new MinecraftVersion(1, 19, 4), 762);
map.put(new MinecraftVersion(1, 20, 0), 763);
map.put(new MinecraftVersion(1, 20, 1), 763);
map.put(new MinecraftVersion(1, 20, 2), 764);
map.put(new MinecraftVersion(1, 20, 3), 765);
map.put(new MinecraftVersion(1, 20, 5), 766);
map.put(new MinecraftVersion(1, 21, 0), 767);
return map;
}
@@ -32,12 +32,6 @@ import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.injector.BukkitUnwrapper;
@@ -54,6 +48,11 @@ import com.comphenix.protocol.wrappers.EnumWrappers;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
/**
* Methods and constants specifically used in conjuction with reflecting Minecraft object.
@@ -117,7 +116,7 @@ public final class MinecraftReflection {
private static MethodAccessor asCraftMirror = null;
private static MethodAccessor isEmpty = null;
private static boolean isMojangMapped = false;
private static Boolean isMojangMapped = null;
private MinecraftReflection() {
// No need to make this constructable.
@@ -208,9 +207,6 @@ public final class MinecraftReflection {
}
}
// for now, we're going to say that it's Mojang mapped if the nms world was renamed to ServerLevel
isMojangMapped = getNmsWorldClass().getName().contains("ServerLevel");
return MINECRAFT_FULL_PACKAGE;
} catch (NoSuchMethodException exception) {
throw new IllegalStateException("Cannot find getHandle() in CraftEntity", exception);
@@ -422,6 +418,10 @@ public final class MinecraftReflection {
return is(getPacketClass(), obj);
}
public static boolean isPacketClass(Class<?> clazz) {
return is(getPacketClass(), clazz);
}
/**
* Determine if the given object is assignable to a NetServerHandler (PlayerConnection)
*
@@ -518,7 +518,7 @@ public final class MinecraftReflection {
.getMethodByName("getHandle");
// EntityPlayer is the return type
return setMinecraftClass("EntityPlayer", getHandle.getReturnType());
return setMinecraftClass("server.level.EntityPlayer", getHandle.getReturnType());
} catch (IllegalArgumentException e1) {
throw new RuntimeException("Could not find EntityPlayer class.", e1);
}
@@ -588,7 +588,7 @@ public final class MinecraftReflection {
try {
return getMinecraftClass("world.level.World", "world.level.Level", "World");
} catch (RuntimeException e) {
return setMinecraftClass("World", getWorldServerClass().getSuperclass());
return setMinecraftClass("world.level.World", getWorldServerClass().getSuperclass());
}
}
@@ -648,7 +648,7 @@ public final class MinecraftReflection {
}
public static boolean isBundleDelimiter(Class<?> packetClass) {
Class<?> bundleDelimiterClass = getBundleDelimiterClass().orElse(null);
Class<?> bundleDelimiterClass = getBundleDelimiterClass().orElse(null);
return bundleDelimiterClass != null && (packetClass.equals(bundleDelimiterClass) || bundleDelimiterClass.isAssignableFrom(packetClass));
}
@@ -685,10 +685,12 @@ public final class MinecraftReflection {
* @return The serializer class.
*/
public static Class<?> getStyleSerializerClass() {
return getMinecraftClass("network.chat.ChatModifier$ChatModifierSerializer", "ChatModifier$ChatModifierSerializer");
return getMinecraftClass(
"network.chat.Style$Serializer",
"network.chat.ChatModifier$ChatModifierSerializer",
"ChatModifier$ChatModifierSerializer");
}
/**
* Retrieve the ServerPing class.
*
@@ -729,10 +731,10 @@ public final class MinecraftReflection {
return getMinecraftClass("server.MinecraftServer", "MinecraftServer");
} catch (RuntimeException e) {
// Reset cache and try again
setMinecraftClass("MinecraftServer", null);
resetCacheForNMSClass("server.MinecraftServer");
useFallbackServer();
return getMinecraftClass("MinecraftServer");
return getMinecraftClass("server.MinecraftServer");
}
}
@@ -764,10 +766,10 @@ public final class MinecraftReflection {
return getMinecraftClass("server.players.PlayerList", "PlayerList");
} catch (RuntimeException e) {
// Reset cache and try again
setMinecraftClass("PlayerList", null);
resetCacheForNMSClass("server.players.PlayerList");
useFallbackServer();
return getMinecraftClass("PlayerList");
return getMinecraftClass("server.players.PlayerList");
}
}
@@ -799,7 +801,7 @@ public final class MinecraftReflection {
return getMinecraftClass("world.item.ItemStack", "ItemStack");
} catch (RuntimeException e) {
// Use the handle reference
return setMinecraftClass("ItemStack", FuzzyReflection.fromClass(getCraftItemStackClass(), true)
return setMinecraftClass("world.item.ItemStack", FuzzyReflection.fromClass(getCraftItemStackClass(), true)
.getFieldByName("handle")
.getType());
}
@@ -1036,7 +1038,21 @@ public final class MinecraftReflection {
* @return The team parameters class.
*/
public static Optional<Class<?>> getTeamParametersClass() {
return getOptionalNMS("network.protocol.game.PacketPlayOutScoreboardTeam$b");
Optional<Class<?>> clazz = getOptionalNMS(
"network.protocol.game.ClientboundSetPlayerTeamPacket$Parameters",
"network.protocol.game.PacketPlayOutScoreboardTeam$b"
);
if (!clazz.isPresent()) {
try {
Class<?> clazz1 = PacketType.Play.Server.SCOREBOARD_TEAM.getPacketClass().getClasses()[0];
setMinecraftClass("network.protocol.game.ClientboundSetPlayerTeamPacket$Parameters", clazz1);
return Optional.of(clazz1);
} catch (Exception ignored) {
}
}
return clazz;
}
/**
@@ -1045,7 +1061,11 @@ public final class MinecraftReflection {
* @return The component style class.
*/
public static Class<?> getComponentStyleClass() {
return getMinecraftClass("network.chat.ChatModifier", "ChatModifier");
return getMinecraftClass(
"network.chat.Style",
"network.chat.ChatModifier",
"ChatModifier"
);
}
/**
@@ -1169,19 +1189,21 @@ public final class MinecraftReflection {
public static Class<?> getPlayerInfoDataClass() {
try {
return getMinecraftClass(
"network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData",
"network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate",
"PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
} catch (Exception ex) {
// todo: ClientboundPlayerInfoUpdatePacket$b, maybe get this via field type
"network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry",
"network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData",
"network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate",
"PacketPlayOutPlayerInfo$PlayerInfoData",
"PlayerInfoData"
);
} catch (Exception ignored) {
return setMinecraftClass(
"network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData",
"network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry",
PacketType.Play.Server.PLAYER_INFO.getPacketClass().getClasses()[0]);
}
}
static Class<?> getOrInferMinecraftClass(String className, Supplier<Class<?>> supplier) {
return getOptionalNMS(className).orElseGet(() -> {
static Class<?> getOrInferMinecraftClass(String className, Supplier<Class<?>> supplier, String... aliases) {
return getOptionalNMS(className, aliases).orElseGet(() -> {
Class<?> clazz = supplier.get();
return setMinecraftClass(className, clazz);
});
@@ -1496,16 +1518,22 @@ public final class MinecraftReflection {
* Retrieves a nullable NMS (net.minecraft.server) class. We will attempt to
* look up the class and its aliases, but will return null if none is found.
*
* @deprecated - Use getOptionalNMS where possible
* @param className NMS class name
* @param aliases Potential aliases
* @return The class, or null if not found
*/
@Deprecated
public static Class<?> getNullableNMS(String className, String... aliases) {
return getOptionalNMS(className, aliases).orElse(null);
}
private static void resetCacheForNMSClass(String className) {
if (minecraftPackage == null) {
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
}
minecraftPackage.removePackageClass(className);
}
/**
* Set the class object for the specific Minecraft class.
*
@@ -1822,7 +1850,7 @@ public final class MinecraftReflection {
}
public static Class<?> getCraftServer() {
return getCraftBukkitClass("CraftServer");
return getCraftBukkitClass("CraftServer");
}
public static Class<?> getHolderLookupProviderClass() {
@@ -1834,26 +1862,31 @@ public final class MinecraftReflection {
}
public static Class<?> getProtocolInfoClass() {
return getMinecraftClass("network.ProtocolInfo");
return getMinecraftClass("network.ProtocolInfo");
}
public static Class<?> getProtocolInfoUnboundClass() {
return getMinecraftClass("network.ProtocolInfo$a" /* Spigot Mappings */, "network.ProtocolInfo$Unbound" /* Mojang Mappings */);
return getMinecraftClass("network.ProtocolInfo$a" /* Spigot Mappings */, "network.ProtocolInfo$Unbound" /* Mojang Mappings */);
}
public static Class<?> getPacketFlowClass() {
return getMinecraftClass("network.protocol.EnumProtocolDirection" /* Spigot Mappings */, "network.protocol.PacketFlow" /* Mojang Mappings */);
return getMinecraftClass("network.protocol.EnumProtocolDirection" /* Spigot Mappings */, "network.protocol.PacketFlow" /* Mojang Mappings */);
}
public static Class<?> getStreamCodecClass() {
return getMinecraftClass("network.codec.StreamCodec");
return getMinecraftClass("network.codec.StreamCodec");
}
public static Optional<Class<?>> getRegistryFriendlyByteBufClass() {
return getOptionalNMS("network.RegistryFriendlyByteBuf");
return getOptionalNMS("network.RegistryFriendlyByteBuf");
}
public static boolean isMojangMapped() {
if (isMojangMapped == null) {
String nmsWorldName = getWorldServerClass().getName();
isMojangMapped = nmsWorldName.contains("ServerLevel");
}
return isMojangMapped;
}
}
@@ -36,6 +36,11 @@ import org.bukkit.Server;
* @author Kristian
*/
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
/**
* Version 1.21.0 - the tricky trials update
*/
public static final MinecraftVersion v1_21_0 = new MinecraftVersion("1.21.0");
/**
* Version 1.20.5 - the cookie and transfer packet update
*/
@@ -144,7 +149,7 @@ public final class MinecraftVersion implements Comparable<MinecraftVersion>, Ser
/**
* The latest release version of minecraft.
*/
public static final MinecraftVersion LATEST = v1_20_5;
public static final MinecraftVersion LATEST = v1_21_0;
// used when serializing
private static final long serialVersionUID = -8695133558996459770L;
@@ -1,5 +1,12 @@
package com.comphenix.protocol.utility;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.Base64;
import com.comphenix.protocol.injector.netty.NettyByteBufAdapter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
@@ -8,14 +15,10 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.comphenix.protocol.wrappers.nbt.NbtType;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.ReferenceCountUtil;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Base64;
import org.bukkit.inventory.ItemStack;
/**
@@ -148,9 +151,14 @@ public class StreamSerializer {
*/
public void serializeString(DataOutputStream output, String text) {
if (WRITE_STRING_METHOD == null) {
WRITE_STRING_METHOD = Accessors.getMethodAccessor(FuzzyReflection
.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true)
.getMethodByParameters("writeString", String.class));
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), false);
WRITE_STRING_METHOD = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactType(String.class)
.parameterCount(1)
.returnDerivedOf(ByteBuf.class)
.requireModifier(Modifier.PUBLIC)
.banModifier(Modifier.STATIC)
.build()));
}
ByteBuf buf = NettyByteBufAdapter.packetWriter(output);
@@ -223,15 +231,15 @@ public class StreamSerializer {
* @return A deserialized item stack, or NULL if the serialized ItemStack was also NULL.
*/
public ItemStack deserializeItemStackFromByteArray(byte[] input) {
if (READ_ITEM_METHOD == null) {
READ_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection
.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true)
.getMethodByReturnTypeAndParameters("readItemStack", MinecraftReflection.getItemStackClass()));
}
ByteBuf buf = Unpooled.wrappedBuffer(input);
Object serializer = MinecraftReflection.getPacketDataSerializer(buf);
if (READ_ITEM_METHOD == null) {
READ_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection
.fromClass(serializer.getClass(), false)
.getMethodByReturnTypeAndParameters("readItemStack", MinecraftReflection.getItemStackClass()));
}
try {
// unwrap the item
Object nmsItem = READ_ITEM_METHOD.invoke(serializer);
@@ -254,6 +262,7 @@ public class StreamSerializer {
* @throws IOException If the operation fails due to reflection problems.
*/
public void serializeItemStack(DataOutputStream output, ItemStack stack) throws IOException {
// TODO this functionality was replaced by the CODEC field in the nms ItemStack
if (WRITE_ITEM_METHOD == null) {
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection
.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true)
@@ -16,14 +16,22 @@
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.Either.Left;
import com.comphenix.protocol.wrappers.Either.Right;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
import java.lang.ref.WeakReference;
import java.lang.reflect.*;
import java.util.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -32,6 +40,7 @@ import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.injector.PacketConstructor;
import com.comphenix.protocol.injector.PacketConstructor.Unwrapper;
@@ -47,8 +56,11 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.Either.Left;
import com.comphenix.protocol.wrappers.Either.Right;
import com.comphenix.protocol.wrappers.EnumWrappers.Dimension;
import com.comphenix.protocol.wrappers.EnumWrappers.FauxEnumConverter;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
@@ -56,7 +68,6 @@ import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Sound;
@@ -1116,29 +1127,30 @@ public class BukkitConverters {
static MethodAccessor getSoundEffect = null;
static FieldAccessor soundKey = null;
static MethodAccessor getSoundEffectBySound = null;
static MethodAccessor getSoundByEffect = null;
static MethodAccessor bukkitToMinecraft = null;
static MethodAccessor minecraftToBukkit = null;
static Map<String, Sound> soundIndex = null;
public static EquivalentConverter<Sound> getSoundConverter() {
// Try to create sound converter for new versions greater 1.16.4
if (MinecraftVersion.NETHER_UPDATE_4.atOrAbove()) {
if (getSoundEffectBySound == null || getSoundByEffect == null) {
if (bukkitToMinecraft == null && minecraftToBukkit == null) {
Class<?> craftSound = MinecraftReflection.getCraftSoundClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(craftSound, true);
Class<?> soundEvent = MinecraftReflection.getSoundEffectClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(craftSound, false);
getSoundEffectBySound = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
"getSoundEffect",
MinecraftReflection.getSoundEffectClass(),
Sound.class
));
bukkitToMinecraft = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.returnTypeExact(soundEvent)
.parameterExactArray(Sound.class)
.requireModifier(Modifier.STATIC)
.build()));
getSoundByEffect = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
"getBukkit",
Sound.class,
MinecraftReflection.getSoundEffectClass()
));
minecraftToBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.returnTypeExact(Sound.class)
.parameterExactArray(soundEvent)
.requireModifier(Modifier.STATIC)
.build()));
}
return ignoreNull(new EquivalentConverter<Sound>() {
@@ -1150,13 +1162,13 @@ public class BukkitConverters {
@Override
public Object getGeneric(Sound specific) {
return getSoundEffectBySound.invoke(null, specific);
return bukkitToMinecraft.invoke(null, specific);
}
@Override
public Sound getSpecific(Object generic) {
try {
return (Sound) getSoundByEffect.invoke(null, generic);
return (Sound) minecraftToBukkit.invoke(null, generic);
} catch (IllegalStateException ex) {
if (ex.getCause() instanceof NullPointerException || ex.getCause() instanceof NoSuchElementException) {
// "null" sounds cause NPEs inside getSoundByEffect
@@ -1,5 +1,16 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.ProtocolLogger;
@@ -11,21 +22,11 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Represents a generic enum converter.
* @author Kristian
@@ -661,7 +662,7 @@ public abstract class EnumWrappers {
INITIALIZED = true;
PROTOCOL_CLASS = getEnum(PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass(), 0);
PROTOCOL_CLASS = MinecraftReflection.getEnumProtocolClass();
CLIENT_COMMAND_CLASS = getEnum(PacketType.Play.Client.CLIENT_COMMAND.getPacketClass(), 0);
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
@@ -736,8 +737,12 @@ public abstract class EnumWrappers {
CHAT_TYPE_CLASS = getEnum(PacketType.Play.Server.CHAT.getPacketClass(), 0);
ENTITY_POSE_CLASS = MinecraftReflection.getNullableNMS("world.entity.EntityPose", "world.entity.Pose", "EntityPose");
DISPLAY_SLOT_CLASS = MinecraftReflection.getNullableNMS("world.scores.DisplaySlot");
RENDER_TYPE_CLASS = MinecraftReflection.getNullableNMS("world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay", "IScoreboardCriteria$EnumScoreboardHealthDisplay");
CHAT_FORMATTING_CLASS = MinecraftReflection.getNullableNMS("EnumChatFormat");
RENDER_TYPE_CLASS = MinecraftReflection.getNullableNMS(
"world.scores.criteria.ObjectiveCriteria$RenderType",
"world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay",
"IScoreboardCriteria$EnumScoreboardHealthDisplay");
CHAT_FORMATTING_CLASS = MinecraftReflection.getNullableNMS("ChatFormatting", "EnumChatFormat");
associate(PROTOCOL_CLASS, Protocol.class, getProtocolConverter());
associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
@@ -53,7 +53,7 @@ public final class InMemoryDataWatcher implements IDataWatcher {
private static FieldAccessor ARRAY_FIELD;
private static ConstructorAccessor CONSTRUCTOR;
private static boolean ARRAY_BACKED = MinecraftVersion.v1_20_4.atOrAbove();
private static boolean ARRAY_BACKED = MinecraftVersion.v1_20_5.atOrAbove();
private static Class<?> SYNCED_DATA_HOLDER_CLASS = ARRAY_BACKED
? MinecraftReflection.getMinecraftClass("network.syncher.SyncedDataHolder")
@@ -16,12 +16,13 @@
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import java.util.Locale;
import java.util.Objects;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
@@ -130,7 +131,7 @@ public class MinecraftKey {
return Objects.hash(prefix, key);
}
private static Constructor<?> constructor = null;
private static ConstructorAccessor CONSTRUCTOR;
public static EquivalentConverter<MinecraftKey> getConverter() {
return new EquivalentConverter<MinecraftKey>() {
@@ -141,19 +142,12 @@ public class MinecraftKey {
@Override
public Object getGeneric(MinecraftKey specific) {
if (constructor == null) {
try {
constructor = MinecraftReflection.getMinecraftKeyClass().getConstructor(String.class, String.class);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to obtain MinecraftKey constructor", e);
}
if (CONSTRUCTOR == null) {
CONSTRUCTOR = Accessors.getConstructorAccessor(MinecraftReflection.getMinecraftKeyClass(),
String.class, String.class);
}
try {
return constructor.newInstance(specific.getPrefix(), specific.getKey());
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to create new MinecraftKey", e);
}
return CONSTRUCTOR.invoke(specific.getPrefix(), specific.getKey());
}
@Override
@@ -1,5 +1,14 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.FuzzyReflection;
@@ -9,37 +18,33 @@ import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.collection.CachedSet;
import com.comphenix.protocol.wrappers.collection.ConvertedSet;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.util.*;
/**
* Represents a single attribute sent in packet 44.
*
* @author Kristian
*/
public class WrappedAttribute extends AbstractWrapper {
public static boolean KEY_WRAPPED = MinecraftVersion.NETHER_UPDATE.atOrAbove();
public static boolean IS_STATIC = MinecraftVersion.CAVES_CLIFFS_1.atOrAbove();
public static boolean IS_IN_HOLDER = MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove();
public final class WrappedAttribute extends AbstractWrapper {
static final boolean KEY_WRAPPED = MinecraftVersion.NETHER_UPDATE.atOrAbove();
static final boolean IS_STATIC = MinecraftVersion.CAVES_CLIFFS_1.atOrAbove();
static final boolean IS_IN_HOLDER = MinecraftVersion.v1_20_5.atOrAbove();
// Shared structure modifier
private static StructureModifier<Object> ATTRIBUTE_MODIFIER;
static StructureModifier<Object> ATTRIBUTE_MODIFIER;
// The one constructor
private static Constructor<?> ATTRIBUTE_CONSTRUCTOR;
static Constructor<?> ATTRIBUTE_CONSTRUCTOR;
/**
* Remaps attributes if needed.
*
* @return the remapped attribute or the attribute itself if no remapping was necessary.
*/
private static String remap(String string) {
static String remap(String string) {
switch (string) {
case "generic.maxHealth": return "generic.max_health";
case "generic.followRange": return "generic.follow_range";
@@ -55,11 +60,7 @@ public class WrappedAttribute extends AbstractWrapper {
}
}
/**
* Reference to the underlying attribute snapshot.
*/
protected Object handle;
protected StructureModifier<Object> modifier;
StructureModifier<Object> modifier;
// Cached computed value
private double computedValue = Double.NaN;
@@ -72,7 +73,7 @@ public class WrappedAttribute extends AbstractWrapper {
*
* @param handle - the NMS instance.
*/
private WrappedAttribute(@Nonnull Object handle) {
WrappedAttribute(@Nonnull Object handle) {
super(MinecraftReflection.getAttributeSnapshotClass());
setHandle(handle);
@@ -83,7 +84,6 @@ public class WrappedAttribute extends AbstractWrapper {
this.modifier = ATTRIBUTE_MODIFIER.withTarget(handle);
}
/**
* Construct a new wrapped attribute around a specific NMS instance.
*
@@ -205,7 +205,7 @@ public class WrappedAttribute extends AbstractWrapper {
* @return TRUE if it does, FALSE otherwise.
*/
public boolean hasModifier(UUID id) {
return getModifiers().contains(WrappedAttributeModifier.newBuilder(id).build());
return getModifierByUUID(id) != null;
}
/**
@@ -214,14 +214,14 @@ public class WrappedAttribute extends AbstractWrapper {
* @param id - the id to look for.
* @return The single attribute modifier with the given ID.
*/
@Nullable
public WrappedAttributeModifier getModifierByUUID(UUID id) {
if (hasModifier(id)) {
for (WrappedAttributeModifier modifier : getModifiers()) {
if (Objects.equal(modifier.getUUID(), id)) {
return modifier;
}
for (WrappedAttributeModifier modifier : getModifiers()) {
if (Objects.equal(modifier.getUUID(), id)) {
return modifier;
}
}
return null;
}
@@ -264,6 +264,10 @@ public class WrappedAttribute extends AbstractWrapper {
return newBuilder(this).modifiers(modifiers).build();
}
public WrappedAttribute shallowClone() {
return newBuilder(this).build();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
@@ -283,7 +287,7 @@ public class WrappedAttribute extends AbstractWrapper {
public int hashCode() {
if (attributeModifiers == null)
getModifiers();
return Objects.hashCode(getAttributeKey(), getBaseValue(), attributeModifiers);
return Objects.hashCode(getAttributeKey(), getBaseValue(), attributeModifiers.hashCode());
}
/**
@@ -363,9 +367,9 @@ public class WrappedAttribute extends AbstractWrapper {
private double baseValue = Double.NaN;
private String attributeKey;
private PacketContainer packet;
private Collection<WrappedAttributeModifier> modifiers = Collections.emptyList();
private Set<WrappedAttributeModifier> modifiers = new HashSet<>();
private Builder(WrappedAttribute template) {
Builder(WrappedAttribute template) {
if (template != null) {
baseValue = template.getBaseValue();
attributeKey = template.getAttributeKey();
@@ -407,7 +411,14 @@ public class WrappedAttribute extends AbstractWrapper {
* @return This builder, for chaining.
*/
public Builder modifiers(Collection<WrappedAttributeModifier> modifiers) {
this.modifiers = Preconditions.checkNotNull(modifiers, "modifiers cannot be NULL - use an empty list instead.");
Preconditions.checkNotNull(modifiers, "modifiers cannot be NULL - use an empty list instead.");
this.modifiers.addAll(modifiers);
return this;
}
public Builder addModifier(WrappedAttributeModifier modifier) {
Preconditions.checkNotNull(modifier, "modifier cannot be NULL.");
this.modifiers.add(modifier);
return this;
}
@@ -416,7 +427,9 @@ public class WrappedAttribute extends AbstractWrapper {
*
* @param packet - the parent packet.
* @return This builder, for chaining.
* @deprecated Removed in 1.17
*/
@Deprecated
public Builder packet(PacketContainer packet) {
if (Preconditions.checkNotNull(packet, "packet cannot be NULL").getType() != PacketType.Play.Server.UPDATE_ATTRIBUTES) {
throw new IllegalArgumentException("Packet must be UPDATE_ATTRIBUTES (44)");
@@ -431,7 +444,7 @@ public class WrappedAttribute extends AbstractWrapper {
* @return Unwrapped modifiers.
*/
private Set<Object> getUnwrappedModifiers() {
final Set<Object> output = new HashSet<>();
Set<Object> output = new HashSet<>();
for (WrappedAttributeModifier modifier : modifiers) {
output.add(modifier.getHandle());
@@ -476,7 +489,9 @@ public class WrappedAttribute extends AbstractWrapper {
throw new IllegalArgumentException("Invalid attribute name: " + this.attributeKey);
}
attributeKey = registry.getHolder(attributeKey);
if (IS_IN_HOLDER) {
attributeKey = registry.getHolder(attributeKey);
}
} else {
attributeKey = this.attributeKey;
}
@@ -1,18 +1,22 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.comphenix.protocol.events.InternalStructure;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import javax.annotation.Nonnull;
import java.lang.reflect.Constructor;
import java.util.UUID;
import java.util.function.Supplier;
import com.google.common.base.Preconditions;
/**
* Represents a wrapper around a AttributeModifier.
@@ -23,6 +27,8 @@ import java.util.function.Supplier;
*/
public class WrappedAttributeModifier extends AbstractWrapper {
private static final boolean OPERATION_ENUM = MinecraftVersion.VILLAGE_UPDATE.atOrAbove();
static final boolean USES_RESOURCE_LOCATION = MinecraftVersion.v1_21_0.atOrAbove();
private static final Class<?> OPERATION_CLASS;
private static final EquivalentConverter<Operation> OPERATION_CONVERTER;
@@ -131,41 +137,24 @@ public class WrappedAttributeModifier extends AbstractWrapper {
throw new IllegalArgumentException("Corrupt operation ID " + id + " detected.");
}
}
// Shared structure modifier
private static StructureModifier<Object> BASE_MODIFIER;
// The constructor we are interested in
private static Constructor<?> ATTRIBUTE_MODIFIER_CONSTRUCTOR;
private static ConstructorAccessor ATTRIBUTE_MODIFIER_CONSTRUCTOR;
// A modifier for the wrapped handler
protected StructureModifier<Object> modifier;
protected InternalStructure modifier;
// Cached values
@Nullable
private final MinecraftKey key;
@Nullable
private final UUID uuid;
private final Supplier<String> name;
private final Operation operation;
private final double amount;
/**
* Construct a new wrapped attribute modifier with no associated handle.
* <p>
* Note that the handle object is not initialized after this constructor.
* @param uuid - the UUID.
* @param name - the human readable name.
* @param amount - the amount.
* @param operation - the operation.
*/
protected WrappedAttributeModifier(UUID uuid, String name, double amount, Operation operation) {
super(MinecraftReflection.getAttributeModifierClass());
// Use the supplied values instead of reading from the NMS instance
this.uuid = uuid;
this.name = () -> name;
this.amount = amount;
this.operation = operation;
}
/**
* Construct an attribute modifier wrapper around a given NMS instance.
* @param handle - the NMS instance.
@@ -176,24 +165,29 @@ public class WrappedAttributeModifier extends AbstractWrapper {
super(MinecraftReflection.getAttributeModifierClass());
setHandle(handle);
initializeModifier(handle);
// Load final values, caching them
this.uuid = (UUID) modifier.withType(UUID.class).read(0);
StructureModifier<String> stringMod = modifier.withType(String.class);
if (stringMod.size() == 0) {
Supplier<String> supplier = (Supplier<String>) modifier.withType(Supplier.class).read(0);
this.name = supplier;
if (USES_RESOURCE_LOCATION) {
this.key = modifier.getMinecraftKeys().read(0);
this.uuid = null;
this.name = () -> null;
} else {
this.name = () -> stringMod.read(0);
this.key = null;
this.uuid = modifier.getUUIDs().read(0);
Optional<String> name = modifier.getStrings().optionRead(0);
if (name.isPresent()) {
this.name = name::get;
} else {
this.name = (Supplier<String>) modifier.getModifier().withType(Supplier.class).read(0);
}
}
this.amount = (Double) modifier.withType(double.class).read(0);
this.amount = modifier.getDoubles().read(0);
if (OPERATION_ENUM) {
this.operation = modifier.withType(OPERATION_CLASS, OPERATION_CONVERTER).readSafely(0);
this.operation = modifier.getModifier().withType(OPERATION_CLASS, OPERATION_CONVERTER).readSafely(0);
} else {
this.operation = Operation.fromId((Integer) modifier.withType(int.class).readSafely(0));
this.operation = Operation.fromId(modifier.getIntegers().readSafely(0));
}
}
@@ -206,12 +200,31 @@ public class WrappedAttributeModifier extends AbstractWrapper {
* @param operation - the operation.
*/
protected WrappedAttributeModifier(@Nonnull Object handle, UUID uuid, String name, double amount, Operation operation) {
this(uuid, name, amount, operation);
super(MinecraftReflection.getAttributeModifierClass());
this.uuid = uuid;
this.name = () -> name;
this.amount = amount;
this.operation = operation;
this.key = null;
// Initialize handle and modifier
setHandle(handle);
initializeModifier(handle);
}
protected WrappedAttributeModifier(Object handle, MinecraftKey key, double amount, Operation operation) {
super(MinecraftReflection.getAttributeModifierClass());
this.uuid = null;
this.key = key;
this.amount = amount;
this.name = key::toString;
this.operation = operation;
setHandle(handle);
initializeModifier(handle);
}
/**
* Construct a new attribute modifier builder.
@@ -256,11 +269,11 @@ public class WrappedAttributeModifier extends AbstractWrapper {
* @param handle - the handle.
*/
private void initializeModifier(@Nonnull Object handle) {
// Initialize modifier
if (BASE_MODIFIER == null) {
BASE_MODIFIER = new StructureModifier<>(MinecraftReflection.getAttributeModifierClass());
}
this.modifier = BASE_MODIFIER.withTarget(handle);
this.modifier = InternalStructure.getConverter().getSpecific(handle);
}
public MinecraftKey getKey() {
return key;
}
/**
@@ -312,7 +325,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
* @param pending - TRUE if it is pending, FALSE otherwise.
*/
public void setPendingSynchronization(boolean pending) {
modifier.withType(boolean.class).write(0, pending);
modifier.getBooleans().write(0, pending);
}
/**
@@ -320,7 +333,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
* @return TRUE if it is, FALSE otherwise.
*/
public boolean isPendingSynchronization() {
return (Boolean) modifier.withType(boolean.class).optionRead(0).orElse(false);
return modifier.getBooleans().optionRead(0).orElse(false);
}
/**
@@ -331,25 +344,34 @@ public class WrappedAttributeModifier extends AbstractWrapper {
* @return TRUE if the given object is the same, FALSE otherwise.
*/
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof WrappedAttributeModifier) {
WrappedAttributeModifier other = (WrappedAttributeModifier) obj;
// Ensure they are equal
return Objects.equal(uuid, other.getUUID());
if (USES_RESOURCE_LOCATION) {
return this.key.equals(other.key);
} else {
return this.uuid.equals(other.uuid);
}
}
return false;
}
@Override
public int hashCode() {
return uuid != null ? uuid.hashCode() : 0;
if (USES_RESOURCE_LOCATION) {
return key != null ? key.hashCode() : 0;
} else {
return uuid != null ? uuid.hashCode() : 0;
}
}
@Override
public String toString() {
return "[amount=" + amount + ", operation=" + operation + ", name='" + name + "', id=" + uuid + ", serialize=" + isPendingSynchronization() + "]";
return "[key=" + key + ", amount=" + amount + ", operation=" + operation + "]";
}
/**
@@ -363,13 +385,15 @@ public class WrappedAttributeModifier extends AbstractWrapper {
private String name = "Unknown";
private double amount;
private UUID uuid;
private MinecraftKey key;
private Builder(WrappedAttributeModifier template) {
if (template != null) {
operation = template.getOperation();
name = template.getName();
amount = template.getAmount();
uuid = template.getUUID();
this.operation = template.getOperation();
this.name = template.getName();
this.amount = template.getAmount();
this.uuid = template.getUUID();
this.key = template.getKey();
}
}
@@ -382,6 +406,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
* @param uuid - the uuid to supply to the new object.
* @return This builder, for chaining.
*/
@Deprecated
public Builder uuid(@Nonnull UUID uuid) {
this.uuid = Preconditions.checkNotNull(uuid, "uuid cannot be NULL.");
return this;
@@ -403,11 +428,21 @@ public class WrappedAttributeModifier extends AbstractWrapper {
* @param name - the name of the modifier.
* @return This builder, for chaining.
*/
@Deprecated
public Builder name(@Nonnull String name) {
this.name = Preconditions.checkNotNull(name, "name cannot be NULL.");
return this;
}
public Builder key(@Nonnull MinecraftKey key) {
this.key = Preconditions.checkNotNull(key, "key cannot be NULL.");
return this;
}
public Builder key(String prefix, String value) {
return key(new MinecraftKey(prefix, value));
}
/**
* Set the amount to modify in the operation.
*
@@ -435,12 +470,28 @@ public class WrappedAttributeModifier extends AbstractWrapper {
// Construct it
try {
// No need to read these values with a modifier
return new WrappedAttributeModifier(
ATTRIBUTE_MODIFIER_CONSTRUCTOR.newInstance(
uuid, name, amount, getOperationParam(operation)),
uuid, name, amount, operation
);
if (USES_RESOURCE_LOCATION) {
if (key == null) {
UUID uuid = this.uuid != null ? this.uuid : UUID.randomUUID();
key = new MinecraftKey("protocollib", uuid.toString());
}
Object handle = ATTRIBUTE_MODIFIER_CONSTRUCTOR.invoke(
MinecraftKey.getConverter().getGeneric(key),
amount,
OPERATION_CONVERTER.getGeneric(operation)
);
return new WrappedAttributeModifier(handle, key, amount, operation);
} else {
Object handle = ATTRIBUTE_MODIFIER_CONSTRUCTOR.invoke(
uuid, name, amount, getOperationParam(operation));
return new WrappedAttributeModifier(
handle,
uuid, name, amount, operation
);
}
} catch (Exception e) {
throw new RuntimeException("Cannot construct AttributeModifier.", e);
}
@@ -451,22 +502,30 @@ public class WrappedAttributeModifier extends AbstractWrapper {
return OPERATION_ENUM ? OPERATION_CONVERTER.getGeneric(operation) : operation.getId();
}
private static Constructor<?> getConstructor() {
FuzzyMethodContract.Builder builder = FuzzyMethodContract
.newBuilder()
.parameterCount(4)
private static ConstructorAccessor getConstructor() {
FuzzyMethodContract.Builder builder = FuzzyMethodContract.newBuilder();
if (USES_RESOURCE_LOCATION) {
builder.parameterCount(3)
.parameterDerivedOf(MinecraftReflection.getMinecraftKeyClass())
.parameterExactType(double.class)
.parameterExactType(OPERATION_CLASS);
} else {
builder.parameterCount(4)
.parameterDerivedOf(UUID.class, 0)
.parameterExactType(String.class, 1)
.parameterExactType(double.class, 2);
if (OPERATION_ENUM) {
builder = builder.parameterExactType(OPERATION_CLASS, 3);
} else {
builder = builder.parameterExactType(int.class, 3);
if (OPERATION_ENUM) {
builder.parameterExactType(OPERATION_CLASS, 3);
} else {
builder.parameterExactType(int.class, 3);
}
}
Constructor<?> ret = FuzzyReflection.fromClass(MinecraftReflection.getAttributeModifierClass(), true)
.getConstructor(builder.build());
ret.setAccessible(true);
return ret;
Constructor<?> ret = FuzzyReflection
.fromClass(MinecraftReflection.getAttributeModifierClass(), true)
.getConstructor(builder.build());
return Accessors.getConstructorAccessor(ret);
}
}
@@ -20,9 +20,11 @@ import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@@ -33,9 +35,12 @@ import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityPose;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
@@ -200,7 +205,7 @@ public class WrappedDataWatcher implements IDataWatcher {
impl.clear();
}
// ---- Object Getters
// ---- 0: byte
/**
* Get a watched byte.
@@ -212,6 +217,12 @@ public class WrappedDataWatcher implements IDataWatcher {
return (Byte) getObject(index);
}
public void setByte(int index, byte value, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.get(Byte.class)), value, update);
}
// ---- short (unused)
/**
* Get a watched short.
*
@@ -222,6 +233,8 @@ public class WrappedDataWatcher implements IDataWatcher {
return (Short) getObject(index);
}
// ---- 1: varint (integer)
/**
* Get a watched integer.
*
@@ -232,6 +245,22 @@ public class WrappedDataWatcher implements IDataWatcher {
return (Integer) getObject(index);
}
public void setInteger(int index, Integer value, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.get(Integer.class)), value, update);
}
// ---- 2: varlong (long)
public Long getLong(int index) {
return (Long) getObject(index);
}
public void setLong(int index, Long value, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.get(Long.class)), value, update);
}
// ---- 3: float
/**
* Get a watched float.
*
@@ -242,6 +271,12 @@ public class WrappedDataWatcher implements IDataWatcher {
return (Float) getObject(index);
}
public void setFloat(int index, Float value, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.get(Float.class)), value, update);
}
// ---- 4: string
/**
* Get a watched string.
*
@@ -252,6 +287,32 @@ public class WrappedDataWatcher implements IDataWatcher {
return (String) getObject(index);
}
public void setString(int index, String value, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.get(String.class)), value, update);
}
// ---- 5: text component
public WrappedChatComponent getChatComponent(int index) {
return (WrappedChatComponent) getObject(index);
}
public void setChatComponent(int index, WrappedChatComponent value, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.getChatComponentSerializer()), value, update);
}
// ---- 6: optional text component
public Optional<WrappedChatComponent> getOptionalChatComponent(int index) {
return (Optional<WrappedChatComponent>) getObject(index);
}
public void setOptionalChatComponent(int index, Optional<WrappedChatComponent> value, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.getChatComponentSerializer(true)), value, update);
}
// ---- 7: slot (item stack)
/**
* Get a watched string.
*
@@ -262,6 +323,158 @@ public class WrappedDataWatcher implements IDataWatcher {
return (ItemStack) getObject(index);
}
public void setItemStack(int index, ItemStack itemStack, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.getItemStackSerializer(false)), itemStack, update);
}
// ---- 8: boolean
public Boolean getBoolean(int index) {
return (Boolean) getObject(index);
}
public void setBoolean(int index, Boolean value, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.get(Boolean.class)), value, update);
}
// ---- 9: rotations
// TODO
// ---- 10: position
public BlockPosition getPosition(int index) {
return (BlockPosition) getObject(index);
}
public void setPosition(int index, BlockPosition position, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.getBlockPositionSerializer(false)), position, update);
}
// ---- 11: optional position
public Optional<BlockPosition> getOptionalPosition(int index) {
return (Optional<BlockPosition>) getObject(index);
}
public void setOptionalPosition(int index, java.util.Optional<BlockPosition> position, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.getBlockPositionSerializer(true)), position, update);
}
// ---- 12: direction
public Direction getDirection(int index) {
return (Direction) getObject(index);
}
public void setDirection(int index, Direction direction, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.getDirectionSerializer()), direction, update);
}
// ---- 13: optional uuid
public Optional<UUID> getOptionalUUID(int index) {
return (Optional<UUID>) getObject(index);
}
public void setOptionalUUID(int index, Optional<UUID> uuid, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.getUUIDSerializer(true)), uuid, update);
}
// ---- 14: block state
public WrappedBlockData getBlockState(int index) {
return (WrappedBlockData) getObject(index);
}
public void setBlockState(int index, WrappedBlockData blockData, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.getBlockDataSerializer(false)), blockData, update);
}
// ---- 15: optional block state
public Optional<WrappedBlockData> getOptionalBlockState(int index) {
return (Optional<WrappedBlockData>) getObject(index);
}
public void setOptionalBlockState(int index, Optional<WrappedBlockData> value, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.getBlockDataSerializer(true)), value, update);
}
// ---- 16: NBT
public NbtCompound getNBTCompound(int index) {
return (NbtCompound) getObject(index);
}
public void setNBTCompound(int index, NbtCompound nbt, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.getNBTCompoundSerializer()), nbt, update);
}
// ---- 17: particle
public WrappedParticle<?> getParticle(int index) {
return (WrappedParticle<?>) getObject(index);
}
public void setParticle(int index, WrappedParticle<?> particle, boolean update) {
// TODO: is ParticleParam correct?
setObject(new WrappedDataWatcherObject(index, Registry.get(MinecraftReflection.getParticleParam(), false)), particle, update);
}
// ---- 18: villager data
public WrappedVillagerData getVillagerData(int index) {
return (WrappedVillagerData) getObject(index);
}
public void setVillagerData(int index, WrappedVillagerData data, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.get(WrappedVillagerData.getNmsClass(), false)), data, update);
}
// ---- 19: optional varint (int)
public Optional<Integer> getOptionalInteger(int index) {
return (Optional<Integer>) getObject(index);
}
public void setOptionalInteger(int index, Optional<Integer> value, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.get(Integer.class, true)), value, update);
}
// ---- 20: pose
public EntityPose getPose(int index) {
return (EntityPose) getObject(index);
}
public void setPose(int index, EntityPose pose, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.get(EnumWrappers.getEntityPoseClass(), false)), pose, update);
}
// TODO: 21-25, 27
// ---- 21: cat variant
// ---- 22: frog variant
// ---- 23: optional global position
// ---- 24: painting variant
// ---- 25: sniffer state
// ---- 26: vector3
public Vector3F getVector3F(int index) {
return (Vector3F) getObject(index);
}
public void setVector3F(int index, Vector3F vector, boolean update) {
setObject(new WrappedDataWatcherObject(index, Registry.get(Vector3F.getMinecraftClass(), false)), vector, update);
}
// ---- 27: quaternion
/**
* Retrieve a watchable object by index.
*
@@ -439,6 +652,22 @@ public class WrappedDataWatcher implements IDataWatcher {
impl.setEntity(entity);
}
/**
* Exports the contents of this data watcher to a list of WrappedDataValues
* for use in the ENTITY_METADATA packet
* @return The data value collection
*/
public List<WrappedDataValue> toDataValueCollection() {
List<WrappedWatchableObject> objects = impl.getWatchableObjects();
List<WrappedDataValue> values = new ArrayList<>(objects.size());
for (WrappedWatchableObject object : objects) {
WrappedDataWatcherObject watcherObj = object.getWatcherObject();
Object value = WrappedWatchableObject.getUnwrapped(object.getRawValue());
values.add(new WrappedDataValue(watcherObj.getIndex(), watcherObj.getSerializer(), value));
}
return values;
}
private static final ImmutableBiMap<Class<?>, Integer> CLASS_TO_ID = new ImmutableBiMap.Builder<Class<?>, Integer>()
.put(Byte.class, 0)
.put(Short.class, 1)
@@ -756,7 +985,9 @@ public class WrappedDataWatcher implements IDataWatcher {
*/
public static class Registry {
private static boolean INITIALIZED = false;
private static List<Serializer> REGISTRY = new ArrayList<>();
private static Map<Class<?>, Serializer> RAW_REGISTRY = null;
private static Map<Class<?>, Serializer> OPTIONAL_REGISTRY = null;
/**
* Gets the first serializer associated with a given class.
@@ -774,13 +1005,13 @@ public class WrappedDataWatcher implements IDataWatcher {
Validate.notNull(clazz,"Class cannot be null!");
initialize();
for (Serializer serializer : REGISTRY) {
if (serializer.getType().equals(clazz)) {
return serializer;
}
Serializer serializer = RAW_REGISTRY.getOrDefault(clazz,
OPTIONAL_REGISTRY.getOrDefault(clazz, null));
if (serializer == null) {
throw new IllegalArgumentException("No serializer found for " + clazz);
}
throw new IllegalArgumentException("No serializer found for " + clazz);
return serializer;
}
/**
@@ -797,16 +1028,12 @@ public class WrappedDataWatcher implements IDataWatcher {
Validate.notNull(clazz, "Class cannot be null!");
initialize();
Validate.notEmpty(REGISTRY, "Registry has no elements!");
for (Serializer serializer : REGISTRY) {
if (serializer.getType().equals(clazz)
&& serializer.isOptional() == optional) {
return serializer;
}
Serializer serializer = optional ? OPTIONAL_REGISTRY.get(clazz) : RAW_REGISTRY.get(clazz);
if (serializer == null) {
throw new IllegalArgumentException("No serializer found for " + (optional ? "Optional<" + clazz + ">" : clazz));
}
throw new IllegalArgumentException("No serializer found for " + (optional ? "Optional<" + clazz + ">" : clazz));
return serializer;
}
/**
@@ -818,7 +1045,13 @@ public class WrappedDataWatcher implements IDataWatcher {
Validate.notNull(handle, "handle cannot be null!");
initialize();
for (Serializer serializer : REGISTRY) {
for (Serializer serializer : RAW_REGISTRY.values()) {
if (serializer.getHandle().equals(handle)) {
return serializer;
}
}
for (Serializer serializer : OPTIONAL_REGISTRY.values()) {
if (serializer.getHandle().equals(handle)) {
return serializer;
}
@@ -834,6 +1067,9 @@ public class WrappedDataWatcher implements IDataWatcher {
return;
}
Map<Class<?>, Serializer> rawRegistry = new HashMap<>();
Map<Class<?>, Serializer> optionalRegistry = new HashMap<>();
List<Field> candidates = FuzzyReflection.fromClass(MinecraftReflection.getDataWatcherRegistryClass(), true)
.getFieldListByType(MinecraftReflection.getDataWatcherSerializerClass());
for (Field candidate : candidates) {
@@ -867,9 +1103,16 @@ public class WrappedDataWatcher implements IDataWatcher {
throw new RuntimeException("Failed to read serializer: " + candidate.getName());
}
REGISTRY.add(new Serializer(innerClass, serializer, optional));
if (optional) {
optionalRegistry.put(innerClass, new Serializer(innerClass, serializer, true));
} else {
rawRegistry.put(innerClass, new Serializer(innerClass, serializer, false));
}
}
}
RAW_REGISTRY = ImmutableMap.copyOf(rawRegistry);
OPTIONAL_REGISTRY = ImmutableMap.copyOf(optionalRegistry);
}
// ---- Helper methods
@@ -9,6 +9,8 @@ import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Preconditions;
import org.bukkit.Color;
import org.bukkit.Particle;
import org.bukkit.block.data.BlockData;
@@ -30,22 +32,26 @@ public class WrappedParticle<T> {
return;
}
Class<?> particleType = MinecraftReflection.isMojangMapped()
? MinecraftReflection.getParticleTypeClass()
: MinecraftReflection.getParticleClass();
Preconditions.checkNotNull(particleType, "Cannot find ParticleType class (MojMap: " + MinecraftReflection.isMojangMapped() + ")");
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle"));
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(Particle.class)
.parameterExactArray(MinecraftReflection.isMojangMapped()
? MinecraftReflection.getParticleTypeClass()
: MinecraftReflection.getParticleClass())
.parameterExactArray(particleType)
.build();
toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
FuzzyReflection particleParam = FuzzyReflection.fromClass(MinecraftReflection.getParticleParam(), false);
contract = FuzzyMethodContract
.newBuilder()
.returnTypeExact(MinecraftReflection.getParticleClass())
.returnTypeExact(particleType)
.parameterCount(0)
.build();
getType = Accessors.getMethodAccessor(particleParam.getMethod(contract));
@@ -154,8 +154,7 @@ public class WrappedTeamParameters extends AbstractWrapper {
Preconditions.checkNotNull(collisionRule, "Collision rule not set");
Preconditions.checkNotNull(color, "Color not set");
// Not technically a packet, but it has a PacketDataSerializer constructor, so it works fine
Object handle = StructureCache.newPacket(getNmsClassOrThrow());
Object handle = StructureCache.newInstance(getNmsClassOrThrow());
WrappedTeamParameters wrapped = new WrappedTeamParameters(handle);
wrapped.writeComponent(0, displayName);
@@ -14,6 +14,8 @@
*/
package com.comphenix.protocol.wrappers;
import java.util.Optional;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
@@ -23,9 +25,8 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import org.bukkit.inventory.ItemStack;
import java.util.Optional;
import org.bukkit.inventory.ItemStack;
import static com.comphenix.protocol.utility.MinecraftReflection.is;
@@ -80,11 +81,13 @@ public final class WrappedWatchableObject extends AbstractWrapper {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
}
Object nmsValue = getUnwrapped(value);
if (MinecraftReflection.watcherObjectExists()) {
return constructor.invoke(watcherObject.getHandle(), value);
return constructor.invoke(watcherObject.getHandle(), nmsValue);
} else {
// new WatchableObject(classId, index, value)
return constructor.invoke(WrappedDataWatcher.getTypeID(value.getClass()), watcherObject.getIndex(), value);
return constructor.invoke(WrappedDataWatcher.getTypeID(value.getClass()), watcherObject.getIndex(), nmsValue);
}
}
@@ -17,22 +17,6 @@
package com.comphenix.protocol.wrappers.nbt;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
import com.google.common.base.Preconditions;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.inventory.ItemStack;
import javax.annotation.Nonnull;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
@@ -47,6 +31,23 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.annotation.Nonnull;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
import com.google.common.base.Preconditions;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.inventory.ItemStack;
/**
* Factory methods for creating NBT elements, lists and compounds.
@@ -1,5 +1,9 @@
package com.comphenix.protocol.wrappers.nbt.io;
import java.io.DataInput;
import java.io.DataOutput;
import java.lang.reflect.Method;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
@@ -12,9 +16,6 @@ import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.comphenix.protocol.wrappers.nbt.NbtList;
import com.comphenix.protocol.wrappers.nbt.NbtWrapper;
import java.io.DataInput;
import java.io.DataOutput;
import java.lang.reflect.Method;
public class NbtBinarySerializer {
@@ -153,15 +154,23 @@ public class NbtBinarySerializer {
public LoadMethodConfigPhaseUpdate() {
// there are now two methods with the same signature: readAnyTag/readUnnamedTag & writeAnyTag/writeUnnamedTag
// we can only find the correct method here by using the method name... thanks Mojang
String readNbtMethodName = MinecraftReflection.isMojangMapped()
? "readAnyTag"
: "b";
String writeNbtMethodName = MinecraftReflection.isMojangMapped()
? "writeAnyTag"
: "a";
Method readNbtMethod = getUtilityClass().getMethod(FuzzyMethodContract.newBuilder()
.nameExact("b")
.nameExact(readNbtMethodName)
.returnTypeExact(MinecraftReflection.getNBTBaseClass())
.parameterExactArray(DataInput.class, this.readLimitClass)
.build());
this.readNbt = Accessors.getMethodAccessor(readNbtMethod);
Method writeNbtMethod = getUtilityClass().getMethod(FuzzyMethodContract.newBuilder()
.nameExact("a")
.nameExact(writeNbtMethodName)
.parameterExactArray(MinecraftReflection.getNBTBaseClass(), DataOutput.class)
.build());
this.writeNbt = Accessors.getMethodAccessor(writeNbtMethod);
@@ -10,42 +10,36 @@ import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
import com.google.common.util.concurrent.MoreExecutors;
import net.minecraft.SharedConstants;
import net.minecraft.commands.CommandDispatcher;
import net.minecraft.core.IRegistry;
import net.minecraft.core.IRegistryCustom;
import net.minecraft.commands.Commands.CommandSelection;
import net.minecraft.core.LayeredRegistryAccess;
import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.RegistryDataLoader;
import net.minecraft.server.DataPackResources;
import net.minecraft.server.DispenserRegistry;
import net.minecraft.server.Main;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.RegistryLayer;
import net.minecraft.server.ReloadableServerResources;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import net.minecraft.server.level.WorldServer;
import net.minecraft.server.packs.EnumResourcePackType;
import net.minecraft.server.packs.repository.ResourcePackLoader;
import net.minecraft.server.packs.repository.ResourcePackRepository;
import net.minecraft.server.packs.repository.ResourcePackSourceVanilla;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.repository.Pack;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.server.packs.repository.ServerPacksSource;
import net.minecraft.server.packs.resources.CloseableResourceManager;
import net.minecraft.server.packs.resources.MultiPackResourceManager;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.item.enchantment.Enchantments;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R4.CraftLootTable;
import org.bukkit.craftbukkit.v1_20_R4.CraftRegistry;
import org.bukkit.craftbukkit.v1_20_R4.CraftServer;
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_20_R4.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.v1_20_R4.util.Versioning;
import org.bukkit.craftbukkit.v1_21_R1.CraftLootTable;
import org.bukkit.craftbukkit.v1_21_R1.CraftRegistry;
import org.bukkit.craftbukkit.v1_21_R1.CraftServer;
import org.bukkit.craftbukkit.v1_21_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_21_R1.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.v1_21_R1.util.Versioning;
import org.spigotmc.SpigotWorldConfig;
import static org.mockito.ArgumentMatchers.any;
@@ -87,6 +81,8 @@ public class BukkitInitialization {
synchronized (initLock) {
if (initialized) {
return;
} else {
initialized = true;
}
try {
@@ -99,33 +95,33 @@ public class BukkitInitialization {
instance.setPackage();
// Minecraft Data Init
SharedConstants.a(); // .tryDetectVersion()
DispenserRegistry.a(); // .bootStrap()
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
ResourcePackRepository resourcePackRepository = ResourcePackSourceVanilla.c(); // .createVanillaTrustedRepository()
resourcePackRepository.a(); // .reload()
PackRepository resourcePackRepository = ServerPacksSource.createVanillaTrustedRepository();
resourcePackRepository.reload();
ResourceManager resourceManager = new ResourceManager(
EnumResourcePackType.b /* SERVER_DATA */,
resourcePackRepository.c() /* getAvailablePacks() */ .stream().map(ResourcePackLoader::f /* openFull() */).collect(Collectors.toList()));
LayeredRegistryAccess<RegistryLayer> layeredRegistryAccess = RegistryLayer.a(); // .createRegistryAccess()
layeredRegistryAccess = WorldLoader.b(resourceManager, layeredRegistryAccess, RegistryLayer.b /* WORLDGEN */, RegistryDataLoader.a /* WORLDGEN_REGISTRIES */); // .loadAndReplaceLayer()
IRegistryCustom.Dimension registryCustom = layeredRegistryAccess.a().d(); // .compositeAccess().freeze()
// IRegistryCustom.Dimension registryCustom = layeredRegistryAccess.a().c(); // .compositeAccess().freeze()
CloseableResourceManager resourceManager = new MultiPackResourceManager(
PackType.SERVER_DATA,
resourcePackRepository.getAvailablePacks().stream().map(Pack::open).collect(Collectors.toList())
);
LayeredRegistryAccess<RegistryLayer> layeredRegistryAccess = RegistryLayer.createRegistryAccess();
layeredRegistryAccess = WorldLoader.loadAndReplaceLayer(resourceManager, layeredRegistryAccess, RegistryLayer.WORLDGEN, RegistryDataLoader.WORLDGEN_REGISTRIES);
RegistryAccess.Frozen registryCustom = layeredRegistryAccess.compositeAccess().freeze();
DataPackResources dataPackResources = DataPackResources.a(
resourceManager,
layeredRegistryAccess,
FeatureFlagSet.a() /* REGISTRY.allFlags() */,
CommandDispatcher.ServerType.b /* DEDICATED */,
0,
MoreExecutors.directExecutor(),
MoreExecutors.directExecutor()
ReloadableServerResources dataPackResources = ReloadableServerResources.loadResources(
resourceManager,
layeredRegistryAccess,
FeatureFlags.REGISTRY.allFlags(),
CommandSelection.DEDICATED,
0,
MoreExecutors.directExecutor(),
MoreExecutors.directExecutor()
).join();
// dataPackResources.a(registryCustom); // .updateRegistryTags()
dataPackResources.updateRegistryTags();
try {
IRegistry.class.getName();
RegistryAccess.class.getName();
} catch (Throwable ex) {
ex.printStackTrace();
}
@@ -137,7 +133,7 @@ public class BukkitInitialization {
CraftServer mockedServer = mock(CraftServer.class);
DedicatedServer mockedGameServer = mock(DedicatedServer.class);
when(mockedGameServer.bc()/*registryAccess*/).thenReturn(registryCustom);
when(mockedGameServer.registryAccess()).thenReturn(registryCustom);
when(mockedServer.getLogger()).thenReturn(java.util.logging.Logger.getLogger("Minecraft"));
when(mockedServer.getName()).thenReturn("Mock Server");
@@ -148,16 +144,22 @@ public class BukkitInitialization {
when(mockedServer.isPrimaryThread()).thenReturn(true);
when(mockedServer.getItemFactory()).thenReturn(CraftItemFactory.instance());
when(mockedServer.getUnsafe()).thenReturn(CraftMagicNumbers.INSTANCE);
/*when(mockedServer.getLootTable(any())).thenAnswer(invocation -> {
when(mockedServer.getLootTable(any())).thenAnswer(invocation -> {
NamespacedKey key = invocation.getArgument(0);
return new CraftLootTable(key, dataPackResources.b().b(CraftNamespacedKey.toMinecraft(key)));
});*/
when(mockedServer.getRegistry(any())).thenAnswer(invocation -> {
Class<Keyed> registryType = invocation.getArgument(0);
return CraftRegistry.createRegistry(registryType, registryCustom);
return new CraftLootTable(key, dataPackResources.fullRegistries().getLootTable(CraftLootTable.bukkitKeyToMinecraft(key)));
});
WorldServer nmsWorld = mock(WorldServer.class);
when(mockedServer.getRegistry(any())).thenAnswer(invocation -> {
Class<? extends Keyed> registryType = invocation.getArgument(0);
try {
return CraftRegistry.createRegistry(registryType, registryCustom);
} catch (Exception ignored) {
return mock(org.bukkit.Registry.class);
}
});
ServerLevel nmsWorld = mock(ServerLevel.class);
SpigotWorldConfig mockWorldConfig = mock(SpigotWorldConfig.class);
try {
@@ -178,10 +180,8 @@ public class BukkitInitialization {
CraftRegistry.setMinecraftRegistry(registryCustom);
// Init Enchantments
Enchantments.A.getClass();
Enchantments.AQUA_AFFINITY.getClass();
// Enchantment.stopAcceptingRegistrations();
initialized = true;
}
}
@@ -14,29 +14,22 @@
*/
package com.comphenix.protocol;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.PacketType.Play.Server;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import net.minecraft.network.protocol.login.PacketLoginInStart;
import net.minecraft.network.protocol.login.ServerboundHelloPacket;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author dmulloy2
@@ -260,7 +253,7 @@ public class PacketTypeTest {
@Test
public void testLoginStart() {
// This packet is critical for handleLoin
assertEquals(PacketLoginInStart.class, PacketType.Login.Client.START.getPacketClass());
assertEquals(ServerboundHelloPacket.class, PacketType.Login.Client.START.getPacketClass());
}
@Test
@@ -287,24 +280,6 @@ public class PacketTypeTest {
}
}
@Test
@Disabled // TODO -- lots of constructor parameters :(
public void testCreateMapChunk() {
new PacketContainer(PacketType.Play.Server.MAP_CHUNK);
}
@Test
@Disabled // TODO -- ScoreboardObjective parameter in constructor is causing this to fail
public void testCreateScoreboardObjective() {
new PacketContainer(PacketType.Play.Server.SCOREBOARD_OBJECTIVE);
}
@Test
@Disabled // TODO -- Entity parameter in constructor is causing this to fail
public void testCreateEntitySound() {
new PacketContainer(PacketType.Play.Server.ENTITY_SOUND);
}
@Test
public void testPacketCreation() {
List<PacketType> failed = new ArrayList<>();
@@ -313,12 +288,6 @@ public class PacketTypeTest {
continue;
}
if (type == PacketType.Play.Server.ENTITY_SOUND
|| type == PacketType.Play.Server.SCOREBOARD_OBJECTIVE
|| type == PacketType.Play.Server.MAP_CHUNK) {
continue;
}
try {
new PacketContainer(type);
} catch (Exception ex) {
@@ -15,16 +15,14 @@
*/
package com.comphenix.protocol.events;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -41,39 +39,56 @@ import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.cloning.SerializableCloner;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.*;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ComponentConverter;
import com.comphenix.protocol.wrappers.CustomPacketPayloadWrapper;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.MovingObjectPositionBlock;
import com.comphenix.protocol.wrappers.Pair;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedAttribute;
import com.comphenix.protocol.wrappers.WrappedAttributeModifier;
import com.comphenix.protocol.wrappers.WrappedBlockData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedDataValue;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedEnumEntityUseAction;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedMessageSignature;
import com.comphenix.protocol.wrappers.WrappedRegistry;
import com.comphenix.protocol.wrappers.WrappedRemoteChatSessionData;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Lists;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import net.minecraft.core.Holder;
import net.minecraft.core.IRegistry;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.PacketDataSerializer;
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
import net.minecraft.network.protocol.common.custom.BrandPayload;
import net.minecraft.network.protocol.game.PacketPlayOutGameStateChange;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket.AttributeSnapshot;
import net.minecraft.network.protocol.game.ClientboundUpdateMobEffectPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectList;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.ai.attributes.AttributeBase;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeModifier.Operation;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.animal.CatVariant;
import net.minecraft.world.entity.animal.FrogVariant;
import org.apache.commons.lang.SerializationUtils;
@@ -91,17 +106,9 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.assertItemCollectionsEqual;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static com.comphenix.protocol.utility.TestUtils.equivalentItem;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static com.comphenix.protocol.utility.TestUtils.*;
import static org.junit.jupiter.api.Assertions.*;
public class PacketContainerTest {
@@ -199,6 +206,7 @@ public class PacketContainerTest {
}
@Test
@Disabled // TODO ???
public void testGetStringArrays() {
PacketContainer packet = new PacketContainer(PacketType.Play.Client.UPDATE_SIGN);
this.testObjectArray(packet.getStringArrays(), 0,
@@ -334,7 +342,7 @@ public class PacketContainerTest {
}
@Test
// @Disabled // TODO -- handle type is null
// // @Disabled // TODO -- handle type is null
public void testGetDataValueCollectionModifier() {
PacketContainer entityMetadata = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA);
StructureModifier<List<WrappedDataValue>> watchableAccessor = entityMetadata.getDataValueCollectionModifier();
@@ -342,8 +350,8 @@ public class PacketContainerTest {
assertEquals(0, watchableAccessor.read(0).size());
List<WrappedDataValue> values = Lists.newArrayList(
new WrappedDataValue(0, Registry.get(Byte.class), (byte) 21),
new WrappedDataValue(1, Registry.get(String.class), "World"));
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(Byte.class), (byte) 21),
new WrappedDataValue(1, WrappedDataWatcher.Registry.get(String.class), "World"));
// Insert and read back
watchableAccessor.write(0, values);
@@ -371,9 +379,9 @@ public class PacketContainerTest {
}
@Test
@Disabled // TODO
// @Disabled // TODO
public void testSerialization() {
PacketContainer useItem = new PacketContainer(PacketType.Play.Client.USE_ITEM);
PacketContainer useItem = new PacketContainer(PacketType.Play.Client.USE_ITEM_ON);
useItem.getMovingBlockPositions().write(0, new MovingObjectPositionBlock(
new BlockPosition(0, 1, 0),
new Vector(0, 1, 0),
@@ -385,7 +393,7 @@ public class PacketContainerTest {
PacketContainer copy = (PacketContainer) SerializationUtils.clone(useItem);
assertEquals(PacketType.Play.Client.USE_ITEM, copy.getType());
assertEquals(PacketType.Play.Client.USE_ITEM_ON, copy.getType());
assertEquals(Hand.MAIN_HAND, copy.getHands().read(0));
assertEquals(5, copy.getIntegers().read(0));
@@ -501,45 +509,66 @@ public class PacketContainerTest {
}
@Test
@Disabled // TODO -- cloning fails
public void testAttributeList() {
PacketContainer attribute = new PacketContainer(PacketType.Play.Server.UPDATE_ATTRIBUTES);
attribute.getIntegers().write(0, 123); // Entity ID
public void testAttributeCloning() {
PacketContainer container = new PacketContainer(PacketType.Play.Server.UPDATE_ATTRIBUTES);
// Initialize some test data
List<AttributeModifier> modifiers = Lists.newArrayList(
new AttributeModifier(UUID.randomUUID(), "Unknown synced attribute modifier", 10,
AttributeModifier.Operation.a));
WrappedAttribute attribute = WrappedAttribute.newBuilder()
.attributeKey("generic.max_health")
.baseValue(20.0D)
.modifiers(Lists.newArrayList(
WrappedAttributeModifier.newBuilder()
.name("Test")
.amount(1.0D)
.operation(WrappedAttributeModifier.Operation.ADD_NUMBER)
.build()
))
.build();
// Obtain an AttributeSnapshot instance. This is complicated by the fact that AttributeSnapshots
// are inner classes (which is ultimately pointless because AttributeSnapshots don't access any
// members of the packet itself)
PacketPlayOutUpdateAttributes packet = (PacketPlayOutUpdateAttributes) attribute.getHandle();
IRegistry<AttributeBase> registry = BuiltInRegistries.u;
AttributeBase base = registry.a(MinecraftKey.a("generic.max_health"));
AttributeSnapshot snapshot = new AttributeSnapshot(Holder.a(base), 20.0D, modifiers);
attribute.getSpecificModifier(List.class).write(0, Lists.newArrayList(snapshot));
container.getAttributeCollectionModifier().write(0, List.of(attribute));
PacketContainer cloned = attribute.deepClone();
AttributeSnapshot
clonedSnapshot = (AttributeSnapshot) cloned.getSpecificModifier(List.class).read(0).get(0);
ClientboundUpdateAttributesPacket packet = (ClientboundUpdateAttributesPacket) container.getHandle();
// Compare the fields, because apparently the packet is a field in AttributeSnapshot
for (Field field : AttributeSnapshot.class.getDeclaredFields()) {
try {
// Skip the packet
if (field.getType().equals(packet.getClass())) {
continue;
}
PacketContainer cloned = container.deepClone();
ClientboundUpdateAttributesPacket clonedPacket = (ClientboundUpdateAttributesPacket) cloned.getHandle();
field.setAccessible(true);
this.testEquality(field.get(snapshot), field.get(clonedSnapshot));
} catch (AssertionError e) {
throw e;
} catch (Throwable ex) {
ex.printStackTrace();
}
}
assertNotSame(packet, clonedPacket);
assertNotSame(packet.getValues(), clonedPacket.getValues());
List<AttributeSnapshot> values = packet.getValues();
List<AttributeSnapshot> clonedValues = clonedPacket.getValues();
assertEquals(values.size(), clonedValues.size());
assertNotSame(values.get(0), clonedValues.get(0));
AttributeSnapshot snapshot = values.get(0);
AttributeSnapshot clonedSnapshot = clonedValues.get(0);
assertEquals(Attributes.MAX_HEALTH, clonedSnapshot.attribute());
assertEquals(20.0D, clonedSnapshot.base());
assertNotSame(snapshot.modifiers(), clonedSnapshot.modifiers());
}
@Test
public void testReadAttributeList() {
AttributeInstance instance = new AttributeInstance(Attributes.MAX_HEALTH, (__) -> {});
instance.setBaseValue(20.0D);
instance.addTransientModifier(new AttributeModifier(ResourceLocation.parse("protocollib:test"), 1.0,
Operation.ADD_VALUE));
ClientboundUpdateAttributesPacket packet = new ClientboundUpdateAttributesPacket(1234, List.of(instance));
PacketContainer container = PacketContainer.fromPacket(packet);
List<WrappedAttribute> attributes = container.getAttributeCollectionModifier().read(0);
WrappedAttribute attribute = attributes.get(0);
assertEquals("generic.max_health", attribute.getAttributeKey());
assertEquals(20.0D, attribute.getBaseValue());
WrappedAttributeModifier modifier = attribute.getModifiers().iterator().next();
assertEquals(1.0D, modifier.getAmount());
assertEquals("protocollib:test", modifier.getKey().getFullKey());
assertEquals(WrappedAttributeModifier.Operation.ADD_NUMBER, modifier.getOperation());
}
@Test
@@ -566,28 +595,30 @@ public class PacketContainerTest {
@SuppressWarnings("deprecation")
public void testPotionEffect() {
PotionEffect effect = new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 20 * 60, 1);
MobEffect mobEffect = new MobEffect(MobEffects.l, effect.getDuration(),
MobEffectInstance mobEffect = new MobEffectInstance(MobEffects.FIRE_RESISTANCE, effect.getDuration(),
effect.getAmplifier(), effect.isAmbient(),
effect.hasParticles());
int entityId = 42;
ClientboundUpdateMobEffectPacket packet;
// The constructor we want to call
PacketConstructor creator = PacketConstructor.DEFAULT.withPacket(
PacketType.Play.Server.ENTITY_EFFECT, new Class<?>[] { int.class, MobEffect.class, boolean.class });
PacketContainer packet = creator.createPacket(entityId, mobEffect, true);
PacketType.Play.Server.ENTITY_EFFECT, new Class<?>[] { int.class, MobEffectInstance.class, boolean.class });
PacketContainer container = creator.createPacket(entityId, mobEffect, true);
assertEquals(entityId, packet.getIntegers().read(0));
assertEquals(effect.getAmplifier(), packet.getIntegers().read(1));
assertEquals(effect.getDuration(), packet.getIntegers().read(2));
assertEquals(entityId, container.getIntegers().read(0));
assertEquals(effect.getAmplifier(), container.getIntegers().read(1));
assertEquals(effect.getDuration(), container.getIntegers().read(2));
WrappedRegistry registry = WrappedRegistry.getRegistry(MinecraftReflection.getMobEffectListClass());
Object effectList = assertInstanceOf(
MobEffectList.class,
packet.getHolders(MobEffectList.class, InternalStructure.CONVERTER).read(0).getHandle()
MobEffect effectList = assertInstanceOf(
MobEffect.class,
container.getHolders(MobEffect.class, InternalStructure.CONVERTER).read(0).getHandle()
);
assertEquals(effect.getType().getId(), registry.getId(effectList) + 1); // +1 is correct, see CraftPotionEffectType
assertTrue(effectList.getDescriptionId().contains("fire_resistance"));
byte b = 0;
if (effect.isAmbient()) {
@@ -602,7 +633,7 @@ public class PacketContainerTest {
b |= 8;
assertEquals(b, (byte) packet.getBytes().read(0));
assertEquals(b, (byte) container.getBytes().read(0));
}
@Test
@@ -656,6 +687,8 @@ public class PacketContainerTest {
@Disabled // TODO -- need a way to create a structure
public void testInternalStructures() {
PacketContainer container = new PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM);
container.getModifier().writeDefaults();
Optional<InternalStructure> optStruct = container.getOptionalStructures().read(0);
assertTrue(optStruct.isPresent());
InternalStructure struct = optStruct.get();
@@ -695,7 +728,7 @@ public class PacketContainerTest {
@Test
public void testMovingBlockPos() {
PacketContainer container = new PacketContainer(PacketType.Play.Client.USE_ITEM);
PacketContainer container = new PacketContainer(PacketType.Play.Client.USE_ITEM_ON);
Vector vector = new Vector(0, 1, 2);
BlockPosition position = new BlockPosition(3, 4, 5);
@@ -798,7 +831,7 @@ public class PacketContainerTest {
}
@Test
@Disabled // TODO -- can't create MAP_CHUNK packet
// @Disabled // TODO -- can't create MAP_CHUNK packet
public void testMapChunk() {
// this is a special case as we are generating a data serializer class (we only need to construct the packet)
PacketContainer container = new PacketContainer(PacketType.Play.Server.MAP_CHUNK);
@@ -886,17 +919,25 @@ public class PacketContainerTest {
}
Object buffer = MinecraftReflection.createPacketDataSerializer(0);
MinecraftMethods.getPacketWriteByteBufMethod().invoke(cloned.getHandle(), buffer);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(constructed.getType().getPacketClass(), false);
FieldAccessor accessor = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
.requireModifier(Modifier.STATIC)
.requireModifier(Modifier.PUBLIC)
.typeDerivedOf(StreamCodec.class)
.build()));
StreamCodec codec = (StreamCodec) accessor.get(cloned.getHandle());
codec.encode(buffer, cloned.getHandle());
}
private static final Set<PacketType> CLONE_EXPECTED_FAILURES = Set.of();
@Test
@Disabled // TODO -- cloning is borked
public void testCloning() {
// Try constructing all the packets
Set<PacketType> failures = new HashSet<>();
for (PacketType type : PacketType.values()) {
// TODO: try to support chat - for now chat contains to many sub classes to properly clone it
if (type.isDeprecated() || !type.isSupported() || type.name().contains("CUSTOM_PAYLOAD") || type.name().contains("CHAT")
|| type.name().contains("BUNDLE")) {
if (type.isDeprecated() || !type.isSupported()) {
continue;
}
@@ -908,22 +949,22 @@ public class PacketContainerTest {
// Make sure watchable collections can be cloned
if (type == PacketType.Play.Server.ENTITY_METADATA) {
IRegistry<CatVariant> catVariantRegistry = BuiltInRegistries.ak;
Registry<CatVariant> catVariantRegistry = BuiltInRegistries.CAT_VARIANT;
constructed.getDataValueCollectionModifier().write(0, Lists.newArrayList(
new WrappedDataValue(0, Registry.get(Byte.class), (byte) 1),
new WrappedDataValue(0, Registry.get(Float.class), 5F),
new WrappedDataValue(0, Registry.get(String.class), "String"),
new WrappedDataValue(0, Registry.get(Boolean.class), true),
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(Byte.class), (byte) 1),
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(Float.class), 5F),
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(String.class), "String"),
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(Boolean.class), true),
new WrappedDataValue(
0,
Registry.getChatComponentSerializer(true),
0,
WrappedDataWatcher.Registry.getChatComponentSerializer(true),
Optional.of(ComponentConverter.fromBaseComponent(TEST_COMPONENT).getHandle())),
new WrappedDataValue(
0,
Registry.getItemStackSerializer(false),
WrappedDataWatcher.Registry.getItemStackSerializer(false),
BukkitConverters.getItemStackConverter().getGeneric(new ItemStack(Material.WOODEN_AXE))),
new WrappedDataValue(0, Registry.get(CatVariant.class), catVariantRegistry.e(CatVariant.e)),
new WrappedDataValue(0, Registry.get(FrogVariant.class), FrogVariant.a)
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(CatVariant.class), catVariantRegistry.get(CatVariant.ALL_BLACK)),
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(FrogVariant.class), FrogVariant.COLD)
));
} else if (type == PacketType.Play.Server.CHAT || type == PacketType.Login.Server.DISCONNECT) {
constructed.getChatComponents().write(0, ComponentConverter.fromBaseComponent(TEST_COMPONENT));
@@ -932,7 +973,7 @@ public class PacketContainerTest {
} else if (type == PacketType.Play.Server.GAME_STATE_CHANGE) {
constructed.getStructures().write(
0,
InternalStructure.getConverter().getSpecific(PacketPlayOutGameStateChange.a));
InternalStructure.getConverter().getSpecific(ClientboundGameEventPacket.ARROW_HIT_PLAYER));
} else if (type == PacketType.Play.Client.USE_ITEM || type == PacketType.Play.Client.BLOCK_PLACE) {
constructed.getLongs().write(0, 0L); // timestamp of the packet, not sent over the network
}
@@ -952,10 +993,13 @@ public class PacketContainerTest {
serializedCloned.getLongs().write(0, 0L);
}
this.assertPacketsEqualAndSerializable(constructed, serializedCloned);
} catch (Throwable t) {
Assertions.fail("Unable to clone " + type, t);
} catch (Throwable ex) {
failures.add(type);
ex.printStackTrace();
}
}
assertEquals(CLONE_EXPECTED_FAILURES, failures);
}
// Convert to objects that support equals()
@@ -1,25 +1,8 @@
package com.comphenix.protocol.injector;
import java.lang.reflect.Field;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.server.level.ChunkProviderServer;
import net.minecraft.server.level.PlayerChunkMap;
import net.minecraft.server.level.PlayerChunkMap.EntityTracker;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.entity.Entity;
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.setFinalField;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.BeforeAll;
public class EntityUtilitiesTest {
@@ -28,10 +11,11 @@ public class EntityUtilitiesTest {
BukkitInitialization.initializeAll();
}
/*
@Test
public void testReflection() {
CraftWorld bukkit = mock(CraftWorld.class);
WorldServer world = mock(WorldServer.class);
ServerLevel world = mock(ServerLevel.class);
when(bukkit.getHandle()).thenReturn(world);
ChunkProviderServer provider = mock(ChunkProviderServer.class);
@@ -57,4 +41,5 @@ public class EntityUtilitiesTest {
.getField(FuzzyFieldContract.newBuilder().typeExact(Int2ObjectMap.class).build());
setFinalField(chunkMap, trackedEntitiesField, trackerMap);
}
*/
}
@@ -1,15 +1,7 @@
package com.comphenix.protocol.injector.netty.channel;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import io.netty.channel.Channel;
import io.netty.channel.local.LocalServerChannel;
import net.minecraft.network.EnumProtocol;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.protocol.EnumProtocolDirection;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class ChannelProtocolUtilTest {
@@ -6,6 +6,8 @@ import java.util.List;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import net.minecraft.network.protocol.login.LoginPacketTypes;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -1,19 +1,11 @@
package com.comphenix.protocol.utility;
import com.comphenix.protocol.BukkitInitialization;
import com.mojang.authlib.GameProfile;
import net.minecraft.nbt.NBTCompressedStreamTools;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes;
import net.minecraft.network.protocol.status.ServerPing;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.server.network.PlayerConnection;
import net.minecraft.util.MinecraftEncryption;
import net.minecraft.world.level.ChunkCoordIntPair;
import net.minecraft.world.level.block.state.IBlockData;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.AfterAll;
@@ -21,13 +13,9 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
public class MinecraftReflectionTest {
@@ -64,6 +52,7 @@ public class MinecraftReflectionTest {
assertNull(MinecraftReflection.getNullableNMS("ProtocolLib"));
}
/*
@Test
public void testAttributeSnapshot() {
assertEquals(PacketPlayOutUpdateAttributes.AttributeSnapshot.class,
@@ -124,7 +113,7 @@ public class MinecraftReflectionTest {
public void testLoginSignature() {
assertEquals(MinecraftEncryption.b.class, MinecraftReflection.getSaltedSignatureClass());
}
*/
@Test
public void testItemStacks() {
ItemStack stack = new ItemStack(Material.GOLDEN_SWORD);
@@ -2,8 +2,8 @@ package com.comphenix.protocol.utility;
public class MinecraftReflectionTestUtil {
public static final String RELEASE_TARGET = "1.20.6";
public static final String PACKAGE_VERSION = "v1_20_R4";
public static final String RELEASE_TARGET = "1.21.0";
public static final String PACKAGE_VERSION = "v1_21_R1";
public static final String NMS = "net.minecraft";
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
@@ -18,7 +18,7 @@
package com.comphenix.protocol.utility;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftVersion;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -48,7 +48,7 @@ class MinecraftVersionTest {
@Test
void testCurrent() {
assertEquals(MinecraftVersion.v1_20_5, MinecraftVersion.getCurrentVersion());
assertEquals(MinecraftVersion.v1_21_0, MinecraftVersion.getCurrentVersion());
}
@Test
@@ -1,16 +1,15 @@
package com.comphenix.protocol.utility;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
@@ -19,6 +18,11 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class StreamSerializerTest {
@BeforeAll
@@ -35,7 +39,10 @@ public class StreamSerializerTest {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
serializer.serializeString(new DataOutputStream(buffer), initial);
DataInputStream input = new DataInputStream(new ByteArrayInputStream(buffer.toByteArray()));
byte[] bytes = buffer.toByteArray();
assertTrue(bytes.length >= initial.length());
DataInputStream input = new DataInputStream(new ByteArrayInputStream(bytes));
String deserialized = serializer.deserializeString(input, 50);
assertEquals(initial, deserialized);
@@ -60,7 +67,7 @@ public class StreamSerializerTest {
}
@Test
@Disabled // TODO -- replaced with registry friendly bytebuf
@Disabled // TODO -- replaced with stream codecs
public void testItems() throws IOException {
StreamSerializer serializer = new StreamSerializer();
ItemStack initial = new ItemStack(Material.STRING);
@@ -72,7 +79,7 @@ public class StreamSerializerTest {
}
@Test
@Disabled // TODO -- replaced with registry friendly bytebuf
@Disabled // TODO -- replaced with stream codecs
public void testItemMeta() throws IOException {
StreamSerializer serializer = new StreamSerializer();
ItemStack initial = new ItemStack(Material.BLUE_WOOL, 2);
@@ -12,11 +12,6 @@ import static org.junit.jupiter.api.Assertions.assertSame;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import net.minecraft.advancements.AdvancementDisplay;
import net.minecraft.advancements.AdvancementFrameType;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.chat.contents.LiteralContents;
import net.minecraft.world.item.Items;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.BeforeAll;
@@ -24,6 +19,7 @@ import org.junit.jupiter.api.Test;
public class AutoWrapperTest {
/*
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
@@ -121,5 +117,5 @@ public class AutoWrapperTest {
public boolean hidden;
public float x;
public float y;
}
}*/
}
@@ -1,11 +1,14 @@
package com.comphenix.protocol.wrappers;
import java.util.Random;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.TestUtils;
import com.comphenix.protocol.wrappers.Either.Left;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
@@ -17,8 +20,6 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.*;
public class BukkitConvertersTest {
@@ -29,7 +30,7 @@ public class BukkitConvertersTest {
}
@Test
@Disabled // TODO -- enchantment cannot be applied to this itemstack(???)
// @Disabled // TODO -- enchantment cannot be applied to this itemstack(???)
public void testItemStacks() {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD, 16);
item.addEnchantment(Enchantment.SHARPNESS, 4);
@@ -15,17 +15,17 @@ public class ChunkCoordIntPairTest {
@Test
public void test() {
net.minecraft.world.level.ChunkCoordIntPair pair = new net.minecraft.world.level.ChunkCoordIntPair(1, 2);
net.minecraft.world.level.ChunkPos pair = new net.minecraft.world.level.ChunkPos(1, 2);
ChunkCoordIntPair specific = ChunkCoordIntPair.getConverter().getSpecific(pair);
assertEquals(1, specific.getChunkX());
assertEquals(2, specific.getChunkZ());
net.minecraft.world.level.ChunkCoordIntPair roundtrip =
(net.minecraft.world.level.ChunkCoordIntPair) ChunkCoordIntPair.getConverter().
net.minecraft.world.level.ChunkPos roundtrip =
(net.minecraft.world.level.ChunkPos) ChunkCoordIntPair.getConverter().
getGeneric(specific);
assertEquals(1, roundtrip.e);
assertEquals(2, roundtrip.f);
assertEquals(1, roundtrip.x);
assertEquals(2, roundtrip.z);
}
}
@@ -31,8 +31,8 @@ public class EnumWrappersTest {
public void validateAllEnumFieldsAreWrapped() {
Map<Class<?>, EquivalentConverter<?>> nativeEnums = EnumWrappers.getFromNativeMap();
for (Entry<Class<?>, EquivalentConverter<?>> entry : nativeEnums.entrySet()) {
for (Object nativeConstant : entry.getKey().getEnumConstants()) {
try {
try {
for (Object nativeConstant : entry.getKey().getEnumConstants()) {
// yay, generics
EquivalentConverter<Object> converter = (EquivalentConverter<Object>) entry.getValue();
@@ -44,16 +44,15 @@ public class EnumWrappersTest {
assertNotNull(unwrappedValue);
assertEquals(nativeConstant, unwrappedValue);
} catch (Exception ex) {
fail(ex);
// ex.printStackTrace();
}
} catch (Exception ex) {
fail(ex);
}
}
}
@Test
public void testValidity() {
assertEquals(EnumWrappers.INVALID, KNOWN_INVALID);
assertEquals(KNOWN_INVALID, EnumWrappers.INVALID);
}
}
@@ -2,27 +2,27 @@ package com.comphenix.protocol.wrappers;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.cloning.BukkitCloner;
import com.comphenix.protocol.wrappers.WrappedAttributeModifier.Operation;
import com.google.common.collect.Lists;
import net.minecraft.core.Holder;
import net.minecraft.core.IRegistry;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.world.entity.ai.attributes.AttributeBase;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket.AttributeSnapshot;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;
public class WrappedAttributeTest {
@@ -37,27 +37,94 @@ public class WrappedAttributeTest {
@BeforeEach
public void setUp() {
// Create a couple of modifiers
this.doubleModifier =
WrappedAttributeModifier.newBuilder().
name("Double Damage").
amount(1).
operation(Operation.ADD_PERCENTAGE).
build();
this.constantModifier =
WrappedAttributeModifier.newBuilder().
name("Damage Bonus").
amount(5).
operation(Operation.ADD_NUMBER).
build();
this.doubleModifier = WrappedAttributeModifier.newBuilder()
.key("protocollib", "double_damage")
.amount(1.0D)
.operation(Operation.ADD_PERCENTAGE)
.build();
// Create attribute
this.attribute = WrappedAttribute.newBuilder().
attributeKey("generic.attackDamage").
baseValue(2).
packet(new PacketContainer(PacketType.Play.Server.UPDATE_ATTRIBUTES)).
modifiers(Lists.newArrayList(this.constantModifier, this.doubleModifier)).
build();
this.constantModifier = WrappedAttributeModifier.newBuilder()
.key("protocollib", "damage_bonus")
.amount(5.0D)
.operation(Operation.ADD_NUMBER)
.build();
this.attribute = WrappedAttribute.newBuilder()
.attributeKey("generic.attackDamage")
.baseValue(2.0D)
.modifiers(Lists.newArrayList(this.constantModifier, this.doubleModifier))
.build();
}
@Test
public void testCreateHandle() {
WrappedAttribute wrapper = WrappedAttribute.newBuilder()
.baseValue(2.0)
.attributeKey("generic.attackDamage")
.modifiers(List.of(constantModifier, doubleModifier))
.build();
AttributeSnapshot handle = (AttributeSnapshot) wrapper.getHandle();
assertNotNull(handle);
assertEquals(2.0D, handle.base());
Attribute attribute = handle.attribute().value();
assertEquals("attribute.name.generic.attack_damage", attribute.getDescriptionId());
Map<ResourceLocation, AttributeModifier> modifiers = handle.modifiers().stream().collect(Collectors.toMap(m -> m.id(), m -> m));
AttributeModifier modifier1 = modifiers.get(ResourceLocation.parse("protocollib:double_damage"));
AttributeModifier modifier2 = modifiers.get(ResourceLocation.parse("protocollib:damage_bonus"));
assertEquals(1.0D, modifier1.amount());
assertEquals(AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL, modifier1.operation());
assertEquals(5.0D, modifier2.amount());
assertEquals(AttributeModifier.Operation.ADD_VALUE, modifier2.operation());
}
@Test
public void testFromHandle() {
AttributeSnapshot handle = new AttributeSnapshot(
Attributes.ATTACK_SPEED,
5.0D,
List.of(new AttributeModifier(ResourceLocation.parse("protocollib:test"),
1.0D, AttributeModifier.Operation.ADD_VALUE))
);
WrappedAttribute wrapper = WrappedAttribute.fromHandle(handle);
assertEquals("generic.attack_speed", wrapper.getAttributeKey());
assertEquals(5.0D, wrapper.getBaseValue());
WrappedAttributeModifier modifier = wrapper.getModifiers().iterator().next();
assertEquals(1.0D, modifier.getAmount());
assertEquals("protocollib:test", modifier.getKey().getFullKey());
assertEquals(Operation.ADD_NUMBER, modifier.getOperation());
}
@Test
public void testCloning() {
AttributeModifier modifier = new AttributeModifier(
ResourceLocation.parse("protocollib:test"),
1.0D,
AttributeModifier.Operation.ADD_VALUE
);
AttributeSnapshot handle = new AttributeSnapshot(
Attributes.ATTACK_SPEED,
5.0D,
List.of(modifier)
);
AttributeSnapshot clone = (AttributeSnapshot) new BukkitCloner().clone(handle);
assertNotSame(handle, clone);
assertEquals(handle.base(), clone.base());
assertEquals(handle.attribute(), clone.attribute());
assertNotSame(handle.modifiers(), clone.modifiers());
assertEquals(1, clone.modifiers().size());
AttributeModifier cloneModifier = clone.modifiers().iterator().next();
assertSame(modifier, cloneModifier);
}
@Test
@@ -71,7 +138,6 @@ public class WrappedAttributeTest {
}
@Test
@Disabled // TODO -- modifiers are missing (or the hasModifier check is wrong)
public void testAttribute() {
assertEquals(this.attribute, WrappedAttribute.fromHandle(this.getAttributeCopy(this.attribute)));
@@ -97,16 +163,17 @@ public class WrappedAttributeTest {
modifiers.add((AttributeModifier) wrapper.getHandle());
}
IRegistry<AttributeBase> registry = BuiltInRegistries.u;
Registry<Attribute> registry = BuiltInRegistries.ATTRIBUTE;
String attributeKey = attribute.getAttributeKey();
MinecraftKey key = MinecraftKey.a(attributeKey);
AttributeBase base = registry.a(key);
Holder<AttributeBase> holder = registry.e(base);
ResourceLocation key = ResourceLocation.tryParse(attributeKey);
Attribute base = registry.get(key);
Holder<Attribute> holder = registry.wrapAsHolder(base);
return new AttributeSnapshot(holder, attribute.getBaseValue(), modifiers);
}
private AttributeModifier getModifierCopy(WrappedAttributeModifier modifier) {
AttributeModifier.Operation operation = AttributeModifier.Operation.values()[modifier.getOperation().getId()];
return new AttributeModifier(modifier.getUUID(), modifier.getName(), modifier.getAmount(), operation);
return new AttributeModifier(ResourceLocation.parse(modifier.getKey().getFullKey()),
modifier.getAmount(), operation);
}
}
@@ -15,13 +15,14 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.BukkitInitialization;
import net.minecraft.world.level.block.state.IBlockData;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.GlassPane;
import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R4.block.impl.CraftStainedGlassPane;
import org.bukkit.craftbukkit.v1_20_R4.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.v1_21_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_21_R1.block.impl.CraftStainedGlassPane;
import org.bukkit.craftbukkit.v1_21_R1.util.CraftMagicNumbers;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -56,14 +57,14 @@ public class WrappedBlockDataTest {
@Test
public void testDataCreation() {
IBlockData nmsData = CraftMagicNumbers.getBlock(Material.CYAN_STAINED_GLASS_PANE).o();
BlockState nmsData = CraftMagicNumbers.getBlock(Material.CYAN_STAINED_GLASS_PANE).defaultBlockState();
GlassPane data = (GlassPane) CraftBlockData.fromData(nmsData);
data.setFace(BlockFace.EAST, true);
WrappedBlockData wrapper = WrappedBlockData.createData(data);
assertEquals(wrapper.getType(), Material.CYAN_STAINED_GLASS_PANE);
GlassPane back = new CraftStainedGlassPane((IBlockData) wrapper.getHandle());
GlassPane back = new CraftStainedGlassPane((BlockState) wrapper.getHandle());
assertEquals(back.hasFace(BlockFace.EAST), data.hasFace(BlockFace.EAST));
assertEquals(back.hasFace(BlockFace.SOUTH), data.hasFace(BlockFace.SOUTH));
}
@@ -1,13 +1,13 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.BukkitInitialization;
import com.google.gson.JsonElement;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import net.minecraft.EnumChatFormat;
import net.minecraft.network.chat.ChatModifier;
import net.minecraft.ChatFormatting;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -22,7 +22,7 @@ public class WrappedComponentStyleTest {
@Test
public void testComponentStyle() {
ChatModifier style = ChatModifier.a.b(EnumChatFormat.m).a(true);
Object style = net.minecraft.network.chat.Style.EMPTY.applyFormat(ChatFormatting.RED).withBold(true);
WrappedComponentStyle wrapped = new WrappedComponentStyle(style);
JsonElement json = wrapped.getJson();
assertEquals("{\"color\":\"red\",\"bold\":true}", json.toString());
@@ -14,16 +14,23 @@
*/
package com.comphenix.protocol.wrappers;
import java.util.UUID;
import java.util.List;
import java.util.Optional;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import net.minecraft.world.entity.projectile.EntityEgg;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftEgg;
import net.minecraft.world.entity.projectile.ThrownEgg;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftEgg;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -39,12 +46,12 @@ public class WrappedDataWatcherTest {
public static void prepare() {
BukkitInitialization.initializeAll();
EntityEgg nmsEgg = new EntityEgg(null, 0, 0, 0);
ThrownEgg nmsEgg = new ThrownEgg(null, 0, 0, 0);
mockEntity = new CraftEgg(null, nmsEgg);
}
@Test
public void testBytes() {
public void testFromEntity() {
WrappedDataWatcher watcher = WrappedDataWatcher.getEntityWatcher(mockEntity);
WrappedWatchableObject watchable = watcher.getWatchableObject(0);
@@ -54,49 +61,160 @@ public class WrappedDataWatcherTest {
assertEquals(object.getSerializer(), Registry.get(Byte.class));
// Make sure we can set existing objects
watcher.setObject(0, (byte) 21);
assertEquals(21, (byte) watcher.getByte(0));
watcher.setByte(0, (byte) 21, false);
assertEquals((byte) 21, watcher.getByte(0));
assertTrue(watcher.hasIndex(1));
assertFalse(watcher.hasIndex(9999));
}
@Test
public void testPrimitives() {
WrappedDataWatcher watcher = new WrappedDataWatcher();
watcher.setByte(0, (byte) 21, false);
assertEquals((byte) 21, watcher.getByte(0));
watcher.setInteger(1, 37, false);
assertEquals(37, watcher.getInteger(1));
watcher.setFloat(2, 42.1F, false);
assertEquals(42.1F, watcher.getFloat(2));
watcher.setLong(3, 69L, false);
assertEquals(69L, watcher.getLong(3));
watcher.setBoolean(4, true, false);
assertTrue(watcher.getBoolean(4));
assertTrue(watcher.hasIndex(4));
assertFalse(watcher.hasIndex(5));
assertEquals(5, watcher.size());
List<WrappedDataValue> dataValues = watcher.toDataValueCollection();
assertEquals(5, dataValues.size());
assertEquals((byte) 21, dataValues.get(0).getValue());
assertEquals((byte) 21, dataValues.get(0).getRawValue());
assertEquals(37, dataValues.get(1).getValue());
assertEquals(37, dataValues.get(1).getRawValue());
assertEquals(42.1F, dataValues.get(2).getValue());
assertEquals(42.1F, dataValues.get(2).getRawValue());
assertEquals(69L, dataValues.get(3).getValue());
assertEquals(69L, dataValues.get(3).getRawValue());
assertEquals(true, dataValues.get(4).getValue());
assertEquals(true, dataValues.get(4).getRawValue());
}
@Test
public void testStrings() {
WrappedDataWatcher watcher = WrappedDataWatcher.getEntityWatcher(mockEntity);
// Make sure we can create watcher objects
Serializer serializer = Registry.get(String.class);
WrappedDataWatcherObject object = new WrappedDataWatcherObject(3, serializer);
watcher.setObject(object, "Test");
watcher.setString(0, "Test", false);
assertEquals(watcher.getString(3), "Test");
assertEquals("Test", watcher.getString(0));
WrappedDataValue wdv = watcher.toDataValueCollection().get(0);
assertEquals("Test", wdv.getValue());
assertEquals("Test", wdv.getRawValue());
assertEquals(0, wdv.getIndex());
assertNotNull(wdv.getSerializer());
assertFalse(wdv.getSerializer().isOptional());
}
@Test
public void testFloats() {
public void testChatComponent() {
WrappedDataWatcher wrapper = new WrappedDataWatcher();
wrapper.setChatComponent(0, WrappedChatComponent.fromText("Test"), false);
wrapper.setOptionalChatComponent(1, Optional.of(WrappedChatComponent.fromText("Test")), false);
// Make sure we can add new entries
Serializer serializer = Registry.get(Float.class);
WrappedDataWatcherObject object = new WrappedDataWatcherObject(10, serializer);
wrapper.setObject(object, 21.0F);
WrappedChatComponent expected = WrappedChatComponent.fromText("Test");
assertEquals(expected, wrapper.getChatComponent(0));
assertTrue(wrapper.getOptionalChatComponent(1).isPresent());
assertEquals(expected, wrapper.getOptionalChatComponent(1).get());
assertTrue(wrapper.hasIndex(10));
List<WrappedDataValue> wdvs = wrapper.toDataValueCollection();
WrappedDataValue wdv1 = wdvs.get(0);
assertEquals(expected, wdv1.getValue());
WrappedDataValue wdv2 = wdvs.get(1);
assertEquals(expected, ((Optional) wdv2.getValue()).get());
assertEquals("literal{Test}", wdv1.getRawValue().toString());
}
@Test
public void testSerializers() {
Serializer blockPos = Registry.get(net.minecraft.core.BlockPosition.class, false);
Serializer optionalBlockPos = Registry.get(net.minecraft.core.BlockPosition.class, true);
assertNotSame(blockPos, optionalBlockPos);
public void testItemStacks() {
WrappedDataWatcher wrapper = new WrappedDataWatcher();
wrapper.setItemStack(0, new ItemStack(Material.ACACIA_FENCE), false);
assertEquals(Material.ACACIA_FENCE, wrapper.getItemStack(0).getType());
// assertNull(Registry.get(ItemStack.class, false)); // TODO
assertNotNull(Registry.get(UUID.class, true));
assertEquals(MinecraftReflection.getItemStackClass(),
wrapper.getWatchableObject(0).getRawValue().getClass());
WrappedDataValue wdv = wrapper.toDataValueCollection().get(0);
assertEquals(Material.ACACIA_FENCE, ((ItemStack) wdv.getValue()).getType());
Object raw = wdv.getRawValue();
assertEquals(MinecraftReflection.getItemStackClass(), raw.getClass());
}
@Test
public void testHasIndex() {
WrappedDataWatcher watcher = WrappedDataWatcher.getEntityWatcher(mockEntity);
assertTrue(watcher.hasIndex(1));
assertFalse(watcher.hasIndex(9999));
public void testMinecraftObjects() {
WrappedDataWatcher watcher = new WrappedDataWatcher();
watcher.setPosition(0, new BlockPosition(1, 2, 3), false);
assertEquals(new BlockPosition(1, 2, 3), watcher.getPosition(0));
watcher.setOptionalPosition(1, Optional.of(new BlockPosition(4, 5, 6)), false);
assertEquals(Optional.of(new BlockPosition(4, 5, 6)), watcher.getOptionalPosition(1));
watcher.setDirection(2, EnumWrappers.Direction.EAST, false);
assertEquals(EnumWrappers.Direction.EAST, watcher.getDirection(2));
watcher.setBlockState(3, WrappedBlockData.createData(Material.ACACIA_FENCE), false);
assertEquals(Material.ACACIA_FENCE, watcher.getBlockState(3).getType());
watcher.setOptionalBlockState(4, Optional.of(WrappedBlockData.createData(Material.ACACIA_FENCE)), false);
assertEquals(Optional.of(WrappedBlockData.createData(Material.ACACIA_FENCE)), watcher.getOptionalBlockState(4));
watcher.setParticle(5, WrappedParticle.create(Particle.CAMPFIRE_COSY_SMOKE, null), false);
assertEquals(Particle.CAMPFIRE_COSY_SMOKE, watcher.getParticle(5).getParticle());
watcher.setParticle(6, WrappedParticle.create(Particle.BLOCK, WrappedBlockData.createData(Material.AZALEA)), false);
assertEquals(Material.AZALEA, ((WrappedParticle<WrappedBlockData>) watcher.getParticle(6)).getData().getType());
assertEquals(7, watcher.size());
List<WrappedDataValue> wdvs = watcher.toDataValueCollection();
assertEquals(7, wdvs.size());
assertEquals(new BlockPosition(1, 2, 3), wdvs.get(0).getValue());
assertEquals(Optional.of(new BlockPosition(4, 5, 6)), wdvs.get(1).getValue());
assertEquals(EnumWrappers.Direction.EAST, wdvs.get(2).getValue());
assertEquals(Material.ACACIA_FENCE, ((WrappedBlockData) wdvs.get(3).getValue()).getType());
assertEquals(Particle.CAMPFIRE_COSY_SMOKE, ((WrappedParticle) wdvs.get(5).getValue()).getParticle());
}
@Test
public void testNBT() {
WrappedDataWatcher watcher = new WrappedDataWatcher();
NbtBase<Integer> nbt = NbtFactory.of("testTag", 17);
NbtCompound compound = NbtFactory.ofCompound("testCompound", List.of(nbt));
watcher.setNBTCompound(0, compound, false);
NbtCompound roundTrip = watcher.getNBTCompound(0);
assertEquals(17, roundTrip.getInteger("testTag"));
List<WrappedDataValue> wdvs = watcher.toDataValueCollection();
Object rawValue = wdvs.get(0).getRawValue();
assertEquals(MinecraftReflection.getNBTCompoundClass(), rawValue.getClass());
}
@Test
@@ -105,6 +223,10 @@ public class WrappedDataWatcherTest {
WrappedDataWatcher cloned = watcher.deepClone();
assertEquals(watcher.size(), cloned.size());
assertEquals(watcher.getObject(1), cloned.getObject(1));
int size = watcher.size();
for (int i = 0; i < size; i++) {
assertEquals(watcher.getObject(i), cloned.getObject(i));
}
}
}
@@ -22,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author Etrayed
*/
@Disabled // todo: figure this registry update out
// @Disabled // todo: figure this registry update out
public class WrappedLevelChunkDataTest {
@BeforeAll
@@ -1,9 +1,10 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.BukkitInitialization;
import net.minecraft.EnumChatFormat;
import net.minecraft.network.chat.ChatModifier;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.numbers.BlankFormat;
import net.minecraft.network.chat.numbers.FixedFormat;
import net.minecraft.network.chat.numbers.StyledFormat;
@@ -22,13 +23,13 @@ public class WrappedNumberFormatTest {
@Test
void testBlankFormat() {
assertInstanceOf(WrappedNumberFormat.Blank.class, WrappedNumberFormat.fromHandle(BlankFormat.a));
assertEquals(BlankFormat.a, WrappedNumberFormat.blank().getHandle());
assertInstanceOf(WrappedNumberFormat.Blank.class, WrappedNumberFormat.fromHandle(BlankFormat.INSTANCE));
assertEquals(BlankFormat.INSTANCE, WrappedNumberFormat.blank().getHandle());
}
@Test
void testFixedFormat() {
IChatBaseComponent content = IChatBaseComponent.a("Fixed");
Component content = Component.literal("Fixed");
WrappedNumberFormat wrappedHandle = WrappedNumberFormat.fromHandle(new FixedFormat(content));
assertInstanceOf(WrappedNumberFormat.Fixed.class, wrappedHandle);
assertEquals(content, ((WrappedNumberFormat.Fixed) wrappedHandle).getContent().getHandle());
@@ -39,7 +40,8 @@ public class WrappedNumberFormatTest {
@Test
void testStyledFormat() {
ChatModifier style = ChatModifier.a.b(EnumChatFormat.g);
Style style = Style.EMPTY.applyFormat(ChatFormatting.AQUA);
// ChatModifier style = ChatModifier.a.b(EnumChatFormat.g);
WrappedNumberFormat wrappedHandle = WrappedNumberFormat.fromHandle(new StyledFormat(style));
assertInstanceOf(WrappedNumberFormat.Styled.class, wrappedHandle);
assertEquals(style, ((WrappedNumberFormat.Styled) wrappedHandle).getStyle().getHandle());
@@ -17,6 +17,7 @@ import org.junit.jupiter.api.Test;
public class WrappedProfilePublicKeyTest {
/*
private static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
@@ -79,5 +80,5 @@ public class WrappedProfilePublicKeyTest {
assertEquals(keyData.getKey(), wrappedKeyData.getKey());
assertEquals(keyData.getExpireTime(), wrappedKeyData.getExpireTime());
assertArrayEquals(keyData.getSignature(), wrappedKeyData.getSignature());
}
}*/
}
@@ -4,7 +4,7 @@ import com.comphenix.protocol.BukkitInitialization;
import java.util.concurrent.ThreadLocalRandom;
import net.minecraft.util.MinecraftEncryption;
import net.minecraft.util.Crypt;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -28,11 +28,11 @@ class WrappedSaltedSignatureTest {
WrappedSaltedSignature loginSignature = new WrappedSaltedSignature(salt, signature);
Object handle = loginSignature.getHandle();
MinecraftEncryption.b data = assertInstanceOf(MinecraftEncryption.b.class, handle);
Crypt.SaltSignaturePair data = assertInstanceOf(Crypt.SaltSignaturePair.class, handle);
assertTrue(data.a());
assertArrayEquals(signature, data.d());
assertEquals(salt, data.c());
assertTrue(data.isValid());
assertArrayEquals(signature, data.signature());
assertEquals(salt, data.salt());
// test key data unwrapping
WrappedSaltedSignature unwrapped = BukkitConverters.getWrappedSignatureConverter().getSpecific(data);
@@ -44,14 +44,15 @@ class WrappedSaltedSignatureTest {
// test key data wrapping
Object wrappedData = BukkitConverters.getWrappedSignatureConverter().getGeneric(loginSignature);
MinecraftEncryption.b wrapped = assertInstanceOf(MinecraftEncryption.b.class, wrappedData);
Crypt.SaltSignaturePair wrapped = assertInstanceOf(Crypt.SaltSignaturePair.class, wrappedData);
assertTrue(wrapped.a());
assertEquals(loginSignature.getSalt(), wrapped.c());
assertArrayEquals(loginSignature.getSignature(), wrapped.d());
assertArrayEquals(loginSignature.getSaltBytes(), wrapped.b());
assertTrue(wrapped.isValid());
assertEquals(loginSignature.getSalt(), wrapped.salt());
assertArrayEquals(loginSignature.getSignature(), wrapped.signature());
assertArrayEquals(loginSignature.getSaltBytes(), wrapped.saltAsBytes());
}
/*
@Test
void testSignedMessageWithoutSignature() {
long salt = ThreadLocalRandom.current().nextLong();
@@ -83,5 +84,5 @@ class WrappedSaltedSignatureTest {
assertEquals(loginSignature.getSalt(), wrapped.c());
assertArrayEquals(loginSignature.getSignature(), wrapped.d());
assertArrayEquals(loginSignature.getSaltBytes(), wrapped.b());
}
}*/
}
@@ -1,17 +1,16 @@
package com.comphenix.protocol.wrappers;
import java.io.IOException;
import java.util.Optional;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.wrappers.WrappedServerPing.CompressedImage;
import com.google.common.io.Resources;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
@@ -25,21 +24,13 @@ public class WrappedServerPingTest {
}
@Test
@Disabled // TODO MotD is null
public void fullTest() throws IOException {
PacketContainer packet = new PacketContainer(PacketType.Status.Server.SERVER_INFO);
Optional<WrappedServerPing> optionalPing = packet.getServerPings().optionRead(0);
assertTrue(optionalPing.isPresent());
WrappedServerPing serverPing = optionalPing.get();
assertNotNull(serverPing.getMotD());
assertNotNull(serverPing.getFavicon());
assertNotNull(serverPing.getPlayers());
assertNotNull(serverPing.getVersionName());
CompressedImage tux = CompressedImage.fromPng(Resources.getResource("tux.png").openStream());
byte[] original = tux.getDataCopy();
WrappedServerPing serverPing = new WrappedServerPing();
serverPing.setMotD("Hello, this is a test.");
serverPing.setPlayersOnline(5);
serverPing.setPlayersMaximum(10);
@@ -3,15 +3,15 @@ package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.BukkitInitialization;
import io.netty.buffer.Unpooled;
import net.minecraft.network.PacketDataSerializer;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.game.PacketPlayOutOpenBook;
import net.minecraft.network.protocol.game.PacketPlayOutSetSlot;
import net.minecraft.world.EnumHand;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.network.protocol.game.ClientboundOpenBookPacket;
import net.minecraft.world.InteractionHand;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_20_R4.CraftRegistry;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_21_R1.CraftRegistry;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -26,29 +26,29 @@ public class WrappedStreamCodecTests {
@Test
public void testWithItemStack() {
StreamCodec<RegistryFriendlyByteBuf, PacketPlayOutSetSlot> nmsCodec = PacketPlayOutSetSlot.a;
StreamCodec<RegistryFriendlyByteBuf, ClientboundContainerSetSlotPacket> nmsCodec = ClientboundContainerSetSlotPacket.STREAM_CODEC;
WrappedStreamCodec codec = new WrappedStreamCodec(nmsCodec);
RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), CraftRegistry.getMinecraftRegistry());
PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(1, 2, 3, CraftItemStack.asNMSCopy(new ItemStack(Material.GOLDEN_SHOVEL)));
ClientboundContainerSetSlotPacket packet = new ClientboundContainerSetSlotPacket(1, 2, 3, CraftItemStack.asNMSCopy(new ItemStack(Material.GOLDEN_SHOVEL)));
codec.encode(buf, packet);
PacketPlayOutSetSlot roundTrip = (PacketPlayOutSetSlot) codec.decode(buf);
ClientboundContainerSetSlotPacket roundTrip = (ClientboundContainerSetSlotPacket) codec.decode(buf);
assertEquals(Material.GOLDEN_SHOVEL, CraftItemStack.asBukkitCopy(roundTrip.f()).getType());
assertEquals(Material.GOLDEN_SHOVEL, CraftItemStack.asBukkitCopy(roundTrip.getItem()).getType());
}
@Test
public void testWithStandardSerializer() {
StreamCodec<PacketDataSerializer, PacketPlayOutOpenBook> nmsCodec = PacketPlayOutOpenBook.a;
StreamCodec<FriendlyByteBuf, ClientboundOpenBookPacket> nmsCodec = ClientboundOpenBookPacket.STREAM_CODEC;
WrappedStreamCodec codec = new WrappedStreamCodec(nmsCodec);
PacketDataSerializer buf = new PacketDataSerializer(Unpooled.buffer());
PacketPlayOutOpenBook packet = new PacketPlayOutOpenBook(EnumHand.a);
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
ClientboundOpenBookPacket packet = new ClientboundOpenBookPacket(InteractionHand.OFF_HAND);
codec.encode(buf, packet);
PacketPlayOutOpenBook roundTrip = (PacketPlayOutOpenBook) codec.decode(buf);
ClientboundOpenBookPacket roundTrip = (ClientboundOpenBookPacket) codec.decode(buf);
assertEquals(EnumHand.a, roundTrip.b());
assertEquals(InteractionHand.OFF_HAND, roundTrip.getHand());
}
}
@@ -1,9 +1,10 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.BukkitInitialization;
import net.minecraft.EnumChatFormat;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -17,9 +18,9 @@ public class WrappedTeamParametersTest {
@Test
void testTeamParameters() {
IChatBaseComponent displayName = IChatBaseComponent.b("display name");
IChatBaseComponent prefix = IChatBaseComponent.b("prefix");
IChatBaseComponent suffix = IChatBaseComponent.b("suffix");
Component displayName = Component.literal("display name");
Component prefix = Component.literal("prefix");
Component suffix = Component.literal("suffix");
String nametagVisibility = "always";
String collisionRule = "never";
@@ -41,13 +42,13 @@ public class WrappedTeamParametersTest {
assertEquals(EnumWrappers.ChatFormatting.RED, wrapped.getColor());
assertEquals(1, wrapped.getOptions());
PacketPlayOutScoreboardTeam.b handle = (PacketPlayOutScoreboardTeam.b) wrapped.getHandle();
assertEquals(handle.a(), displayName);
assertEquals(handle.f(), prefix);
assertEquals(handle.g(), suffix);
assertEquals(handle.d(), nametagVisibility);
assertEquals(handle.e(), collisionRule);
assertEquals(handle.c(), EnumChatFormat.m);
assertEquals(handle.b(), 1);
ClientboundSetPlayerTeamPacket.Parameters handle = (ClientboundSetPlayerTeamPacket.Parameters) wrapped.getHandle();
assertEquals(handle.getDisplayName(), displayName);
assertEquals(handle.getPlayerPrefix(), prefix);
assertEquals(handle.getPlayerSuffix(), suffix);
assertEquals(handle.getNametagVisibility(), nametagVisibility);
assertEquals(handle.getCollisionRule(), collisionRule);
assertEquals(handle.getColor(), ChatFormatting.RED);
assertEquals(handle.getOptions(), 1);
}
}
@@ -17,23 +17,25 @@
package com.comphenix.protocol.wrappers.nbt;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class NbtFactoryTest {
@BeforeAll
@@ -64,9 +66,9 @@ public class NbtFactoryTest {
}
@Test
@Disabled // TODO
@Disabled // TODO -- item stacks don't have tags anymore
public void testItemTag() {
ItemStack test = new ItemStack(Items.L);
ItemStack test = new ItemStack(Items.ACACIA_DOOR);
org.bukkit.inventory.ItemStack craftTest = MinecraftReflection.getBukkitItemStack(test);
NbtCompound compound = NbtFactory.ofCompound("tag");