Skip to content

Commit

Permalink
Reimplement ItemStack Obfuscation (#11817)
Browse files Browse the repository at this point in the history
Reimplementation of the itemstack obfuscation config that
leverages the component patch map codec to drop
unwanted components on items or replaces them with
sanitized versions.

Co-authored-by: Bjarne Koll <[email protected]>
Co-authored-by: Jake Potrebic <[email protected]>
  • Loading branch information
3 people authored Jan 9, 2025
1 parent efdcaa2 commit 55f3f28
Show file tree
Hide file tree
Showing 17 changed files with 521 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
--- a/net/minecraft/core/component/DataComponentPatch.java
+++ b/net/minecraft/core/component/DataComponentPatch.java
@@ -86,6 +_,11 @@
buffer.writeVarInt(0);
buffer.writeVarInt(0);
} else {
+ // Paper start - data sanitization for items
+ final io.papermc.paper.util.ItemObfuscationSession itemObfuscationSession = value.map.isEmpty()
+ ? null // Avoid thread local lookup of current session if it won't be needed anyway.
+ : io.papermc.paper.util.ItemObfuscationSession.currentSession();
+ // Paper end - data sanitization for items
int i = 0;
int i1 = 0;

@@ -93,7 +_,7 @@
value.map
)) {
if (entry.getValue().isPresent()) {
- i++;
+ if (!io.papermc.paper.util.ItemComponentSanitizer.shouldDrop(itemObfuscationSession, entry.getKey())) i++; // Paper - data sanitization for items
} else {
i1++;
}
@@ -106,6 +_,7 @@
value.map
)) {
Optional<?> optional = entryx.getValue();
+ optional = io.papermc.paper.util.ItemComponentSanitizer.override(itemObfuscationSession, entryx.getKey(), entryx.getValue()); // Paper - data sanitization for items
if (optional.isPresent()) {
DataComponentType<?> dataComponentType = entryx.getKey();
DataComponentType.STREAM_CODEC.encode(buffer, dataComponentType);
@@ -125,7 +_,13 @@
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
);
public static final DataComponentType<ChargedProjectiles> CHARGED_PROJECTILES = register(
- "charged_projectiles", builder -> builder.persistent(ChargedProjectiles.CODEC).networkSynchronized(ChargedProjectiles.STREAM_CODEC).cacheEncoding()
+ "charged_projectiles", builder -> builder.persistent(ChargedProjectiles.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.CHARGED_PROJECTILES).cacheEncoding() // Paper - sanitize charged projectiles
+ "charged_projectiles", builder -> builder.persistent(ChargedProjectiles.CODEC).networkSynchronized(io.papermc.paper.util.OversizedItemComponentSanitizer.CHARGED_PROJECTILES).cacheEncoding() // Paper - sanitize charged projectiles
);
public static final DataComponentType<BundleContents> BUNDLE_CONTENTS = register(
- "bundle_contents", builder -> builder.persistent(BundleContents.CODEC).networkSynchronized(BundleContents.STREAM_CODEC).cacheEncoding()
+ "bundle_contents", builder -> builder.persistent(BundleContents.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.BUNDLE_CONTENTS).cacheEncoding() // Paper - sanitize bundle contents
+ "bundle_contents", builder -> builder.persistent(BundleContents.CODEC).networkSynchronized(io.papermc.paper.util.OversizedItemComponentSanitizer.BUNDLE_CONTENTS).cacheEncoding() // Paper - sanitize bundle contents
);
public static final DataComponentType<PotionContents> POTION_CONTENTS = register(
"potion_contents", builder -> builder.persistent(PotionContents.CODEC).networkSynchronized(PotionContents.STREAM_CODEC).cacheEncoding()
Expand All @@ -18,7 +18,7 @@
);
public static final DataComponentType<ItemContainerContents> CONTAINER = register(
- "container", builder -> builder.persistent(ItemContainerContents.CODEC).networkSynchronized(ItemContainerContents.STREAM_CODEC).cacheEncoding()
+ "container", builder -> builder.persistent(ItemContainerContents.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.CONTAINER).cacheEncoding() // Paper - sanitize container contents
+ "container", builder -> builder.persistent(ItemContainerContents.CODEC).networkSynchronized(io.papermc.paper.util.OversizedItemComponentSanitizer.CONTAINER).cacheEncoding() // Paper - sanitize container contents
);
public static final DataComponentType<BlockItemStateProperties> BLOCK_STATE = register(
"block_state", builder -> builder.persistent(BlockItemStateProperties.CODEC).networkSynchronized(BlockItemStateProperties.STREAM_CODEC).cacheEncoding()
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
}

private static void pack(List<SynchedEntityData.DataValue<?>> dataValues, RegistryFriendlyByteBuf buffer) {
+ try (io.papermc.paper.util.DataSanitizationUtil.DataSanitizer ignored = io.papermc.paper.util.DataSanitizationUtil.start(true)) { // Paper - data sanitization
+ try (io.papermc.paper.util.ItemObfuscationSession ignored = io.papermc.paper.util.ItemObfuscationSession.start(io.papermc.paper.configuration.GlobalConfiguration.get().anticheat.obfuscation.items.binding.level)) { // Paper - data sanitization
for (SynchedEntityData.DataValue<?> dataValue : dataValues) {
dataValue.write(buffer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
buffer.writeVarInt(this.entity);
int size = this.slots.size();

+ try (io.papermc.paper.util.DataSanitizationUtil.DataSanitizer ignored = io.papermc.paper.util.DataSanitizationUtil.start(this.sanitize)) { // Paper - data sanitization
+ try (final io.papermc.paper.util.ItemObfuscationSession ignored = io.papermc.paper.util.ItemObfuscationSession.start(this.sanitize ? io.papermc.paper.configuration.GlobalConfiguration.get().anticheat.obfuscation.items.binding.level : io.papermc.paper.util.ItemObfuscationSession.ObfuscationLevel.NONE)) { // Paper - data sanitization
for (int i = 0; i < size; i++) {
Pair<EquipmentSlot, ItemStack> pair = this.slots.get(i);
EquipmentSlot equipmentSlot = pair.getFirst();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
+ if (value.isEmpty() || value.getItem() == null) { // CraftBukkit - NPE fix itemstack.getItem()
buffer.writeVarInt(0);
} else {
buffer.writeVarInt(value.getCount());
- buffer.writeVarInt(value.getCount());
+ buffer.writeVarInt(io.papermc.paper.util.ItemComponentSanitizer.sanitizeCount(io.papermc.paper.util.ItemObfuscationSession.currentSession(), value, value.getCount())); // Paper - potentially sanitize count
ITEM_STREAM_CODEC.encode(buffer, value.getItemHolder());
+ // Spigot start - filter
+ // value = value.copy();
+ // CraftItemStack.setItemMeta(value, CraftItemStack.getItemMeta(value)); // Paper - This is no longer with raw NBT being handled in metadata
+ // Paper start - adventure; conditionally render translatable components
+ boolean prev = net.minecraft.network.chat.ComponentSerialization.DONT_RENDER_TRANSLATABLES.get();
+ try {
+ try (final io.papermc.paper.util.SafeAutoClosable ignored = io.papermc.paper.util.ItemObfuscationSession.withContext(c -> c.itemStack(value))) { // pass the itemstack as context to the obfuscation session
+ net.minecraft.network.chat.ComponentSerialization.DONT_RENDER_TRANSLATABLES.set(true);
DataComponentPatch.STREAM_CODEC.encode(buffer, value.components.asPatch());
+ } finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ protected ObjectMapper.Factory.Builder createGlobalObjectMapperFactoryBuilder()
}

@MustBeInvokedByOverriders
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder() {
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder(RegistryAccess registryAccess) {
return this.createLoaderBuilder();
}

Expand All @@ -104,7 +104,7 @@ static <T> CheckedFunction<ConfigurationNode, T, SerializationException> reloade
}

public G initializeGlobalConfiguration(final RegistryAccess registryAccess) throws ConfigurateException {
return this.initializeGlobalConfiguration(creator(this.globalConfigClass, true));
return this.initializeGlobalConfiguration(registryAccess, creator(this.globalConfigClass, true));
}

private void trySaveFileNode(YamlConfigurationLoader loader, ConfigurationNode node, String filename) throws ConfigurateException {
Expand All @@ -117,9 +117,9 @@ private void trySaveFileNode(YamlConfigurationLoader loader, ConfigurationNode n
}
}

protected G initializeGlobalConfiguration(final CheckedFunction<ConfigurationNode, G, SerializationException> creator) throws ConfigurateException {
protected G initializeGlobalConfiguration(final RegistryAccess registryAccess, final CheckedFunction<ConfigurationNode, G, SerializationException> creator) throws ConfigurateException {
final Path configFile = this.globalFolder.resolve(this.globalConfigFileName);
final YamlConfigurationLoader loader = this.createGlobalLoaderBuilder()
final YamlConfigurationLoader loader = this.createGlobalLoaderBuilder(registryAccess)
.defaultOptions(this.applyObjectMapperFactory(this.createGlobalObjectMapperFactoryBuilder().build()))
.path(configFile)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
import io.papermc.paper.configuration.constraint.Constraints;
import io.papermc.paper.configuration.type.number.DoubleOr;
import io.papermc.paper.configuration.type.number.IntOr;
import io.papermc.paper.util.ItemObfuscationBinding;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ServerboundPlaceRecipePacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Items;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
Expand All @@ -20,6 +24,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.OptionalInt;
import java.util.Set;

@SuppressWarnings({"CanBeFinal", "FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"})
public class GlobalConfiguration extends ConfigurationPart {
Expand Down Expand Up @@ -69,7 +74,7 @@ public class ChunkLoadingAdvanced extends ConfigurationPart {
)
public int playerMaxConcurrentChunkGenerates = 0;
}
static void set(GlobalConfiguration instance) {
static void set(final GlobalConfiguration instance) {
GlobalConfiguration.instance = instance;
}

Expand Down Expand Up @@ -354,4 +359,41 @@ public class BlockUpdates extends ConfigurationPart {
public boolean disableChorusPlantUpdates = false;
public boolean disableMushroomBlockUpdates = false;
}

public Anticheat anticheat;

public class Anticheat extends ConfigurationPart {

public Obfuscation obfuscation;

public class Obfuscation extends ConfigurationPart {
public Items items;

public class Items extends ConfigurationPart {

public boolean enableItemObfuscation = false;
public ItemObfuscationBinding.AssetObfuscationConfiguration allModels = new ItemObfuscationBinding.AssetObfuscationConfiguration(
true,
Set.of(DataComponents.LODESTONE_TRACKER),
Set.of()
);

public Map<ResourceLocation, ItemObfuscationBinding.AssetObfuscationConfiguration> modelOverrides = Map.of(
Objects.requireNonNull(net.minecraft.world.item.Items.ELYTRA.components().get(DataComponents.ITEM_MODEL)),
new ItemObfuscationBinding.AssetObfuscationConfiguration(
true,
Set.of(DataComponents.DAMAGE),
Set.of()
)
);

public transient ItemObfuscationBinding binding;

@PostProcess
public void bindDataSanitizer() {
this.binding = new ItemObfuscationBinding(this);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.papermc.paper.configuration.serializer.EnumValueSerializer;
import io.papermc.paper.configuration.serializer.NbtPathSerializer;
import io.papermc.paper.configuration.serializer.PacketClassSerializer;
import io.papermc.paper.configuration.serializer.ResourceLocationSerializer;
import io.papermc.paper.configuration.serializer.StringRepresentableSerializer;
import io.papermc.paper.configuration.serializer.collections.FastutilMapSerializer;
import io.papermc.paper.configuration.serializer.collections.MapSerializer;
Expand Down Expand Up @@ -48,6 +49,7 @@
import java.util.function.Function;
import java.util.function.Supplier;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
Expand Down Expand Up @@ -180,6 +182,7 @@ private static ConfigurationOptions defaultOptions(ConfigurationOptions options)
.register(Duration.SERIALIZER)
.register(DurationOrDisabled.SERIALIZER)
.register(NbtPathSerializer.SERIALIZER)
.register(ResourceLocationSerializer.INSTANCE)
);
}

Expand All @@ -193,16 +196,17 @@ private static ObjectMapper.Factory.Builder defaultGlobalFactoryBuilder(ObjectMa
}

@Override
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder() {
return super.createGlobalLoaderBuilder()
.defaultOptions(PaperConfigurations::defaultGlobalOptions);
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder(RegistryAccess registryAccess) {
return super.createGlobalLoaderBuilder(registryAccess)
.defaultOptions((options) -> defaultGlobalOptions(registryAccess, options));
}

private static ConfigurationOptions defaultGlobalOptions(ConfigurationOptions options) {
private static ConfigurationOptions defaultGlobalOptions(RegistryAccess registryAccess, ConfigurationOptions options) {
return options
.header(GLOBAL_HEADER)
.serializers(builder -> builder
.register(new PacketClassSerializer())
.register(new RegistryValueSerializer<>(new TypeToken<DataComponentType<?>>() {}, registryAccess, Registries.DATA_COMPONENT_TYPE, false))
);
}

Expand Down Expand Up @@ -316,7 +320,7 @@ protected boolean isConfigType(final Type type) {

public void reloadConfigs(MinecraftServer server) {
try {
this.initializeGlobalConfiguration(reloader(this.globalConfigClass, GlobalConfiguration.get()));
this.initializeGlobalConfiguration(server.registryAccess(), reloader(this.globalConfigClass, GlobalConfiguration.get()));
this.initializeWorldDefaultsConfiguration(server.registryAccess());
for (ServerLevel level : server.getAllLevels()) {
this.createWorldConfig(createWorldContextMap(level), reloader(this.worldConfigClass, level.paperConfig()));
Expand Down Expand Up @@ -454,9 +458,9 @@ public static YamlConfiguration loadLegacyConfigFile(File configFile) throws Exc
}

@VisibleForTesting
static ConfigurationNode createForTesting() {
static ConfigurationNode createForTesting(RegistryAccess registryAccess) {
ObjectMapper.Factory factory = defaultGlobalFactoryBuilder(ObjectMapper.factoryBuilder()).build();
ConfigurationOptions options = defaultGlobalOptions(defaultOptions(ConfigurationOptions.defaults()))
ConfigurationOptions options = defaultGlobalOptions(registryAccess, defaultOptions(ConfigurationOptions.defaults()))
.serializers(builder -> builder.register(type -> ConfigurationPart.class.isAssignableFrom(erase(type)), factory.asTypeSerializer()));
return BasicConfigurationNode.root(options);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,6 @@ public boolean isDefault() {

public class Anticheat extends ConfigurationPart {

public Obfuscation obfuscation;

public class Obfuscation extends ConfigurationPart {
public Items items = new Items();
public class Items extends ConfigurationPart {
public boolean hideItemmeta = false;
public boolean hideDurability = false;
public boolean hideItemmetaWithVisualEffects = false;
}
}

public AntiXray antiXray;

public class AntiXray extends ConfigurationPart {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.papermc.paper.configuration.serializer;

import java.lang.reflect.Type;
import java.util.function.Predicate;
import net.minecraft.resources.ResourceLocation;
import org.spongepowered.configurate.serialize.ScalarSerializer;
import org.spongepowered.configurate.serialize.SerializationException;

public class ResourceLocationSerializer extends ScalarSerializer<ResourceLocation> {

public static final ScalarSerializer<ResourceLocation> INSTANCE = new ResourceLocationSerializer();

private ResourceLocationSerializer() {
super(ResourceLocation.class);
}

@Override
public ResourceLocation deserialize(final Type type, final Object obj) throws SerializationException {
return ResourceLocation.read(obj.toString()).getOrThrow(s -> new SerializationException(ResourceLocation.class, s));
}

@Override
protected Object serialize(final ResourceLocation item, final Predicate<Class<?>> typeSupported) {
return item.toString();
}
}
Loading

0 comments on commit 55f3f28

Please sign in to comment.