mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-26 14:30:17 +01:00
Push initial datacomponent obfuscation logic
This general principal is that there is no mutation of the patched map, only swapping of the valid values. Additional work will need to be done to support hiding enchantments whilst keeping the glint, as we either have to append an enchantment, or need to add a glint override.. which kinda defeats the purpose above. Cant add a vanilla enchantment because those can be removed! Further investigation needed.
This commit is contained in:
parent
aa2c52baac
commit
37a20964c3
8 changed files with 213 additions and 54 deletions
|
@ -0,0 +1,25 @@
|
|||
--- a/net/minecraft/core/component/DataComponentType.java
|
||||
+++ b/net/minecraft/core/component/DataComponentType.java
|
||||
@@ -80,11 +_,11 @@
|
||||
static class SimpleType<T> implements DataComponentType<T> {
|
||||
@Nullable
|
||||
private final Codec<T> codec;
|
||||
- private final StreamCodec<? super RegistryFriendlyByteBuf, T> streamCodec;
|
||||
+ private final java.util.function.Supplier<StreamCodec<? super RegistryFriendlyByteBuf, T>> streamCodec; // Paper
|
||||
|
||||
SimpleType(@Nullable Codec<T> codec, StreamCodec<? super RegistryFriendlyByteBuf, T> streamCodec) {
|
||||
this.codec = codec;
|
||||
- this.streamCodec = streamCodec;
|
||||
+ this.streamCodec = com.google.common.base.Suppliers.memoize(() -> io.papermc.paper.util.DataSanitizationUtil.get(this, streamCodec)); // Paper
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -95,7 +_,7 @@
|
||||
|
||||
@Override
|
||||
public StreamCodec<? super RegistryFriendlyByteBuf, T> streamCodec() {
|
||||
- return this.streamCodec;
|
||||
+ return this.streamCodec.get(); // Paper
|
||||
}
|
||||
|
||||
@Override
|
|
@ -1,24 +0,0 @@
|
|||
--- a/net/minecraft/core/component/DataComponents.java
|
||||
+++ b/net/minecraft/core/component/DataComponents.java
|
||||
@@ -180,10 +_,10 @@
|
||||
"map_post_processing", builder -> builder.networkSynchronized(MapPostProcessing.STREAM_CODEC)
|
||||
);
|
||||
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
|
||||
);
|
||||
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
|
||||
);
|
||||
public static final DataComponentType<PotionContents> POTION_CONTENTS = register(
|
||||
"potion_contents", builder -> builder.persistent(PotionContents.CODEC).networkSynchronized(PotionContents.STREAM_CODEC).cacheEncoding()
|
||||
@@ -250,7 +_,7 @@
|
||||
"pot_decorations", builder -> builder.persistent(PotDecorations.CODEC).networkSynchronized(PotDecorations.STREAM_CODEC).cacheEncoding()
|
||||
);
|
||||
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
|
||||
);
|
||||
public static final DataComponentType<BlockItemStateProperties> BLOCK_STATE = register(
|
||||
"block_state", builder -> builder.persistent(BlockItemStateProperties.CODEC).networkSynchronized(BlockItemStateProperties.STREAM_CODEC).cacheEncoding()
|
|
@ -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.DataSanitizationUtil.sanitizeCount(value, value.getCount())); // Paper
|
||||
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 (io.papermc.paper.util.DataSanitizationUtil.SafeAutoClosable unused = io.papermc.paper.util.DataSanitizationUtil.passContext(value)) {
|
||||
+ net.minecraft.network.chat.ComponentSerialization.DONT_RENDER_TRANSLATABLES.set(true);
|
||||
DataComponentPatch.STREAM_CODEC.encode(buffer, value.components.asPatch());
|
||||
+ } finally {
|
||||
|
|
|
@ -80,7 +80,7 @@ public abstract class Configurations<G, W> {
|
|||
}
|
||||
|
||||
@MustBeInvokedByOverriders
|
||||
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder() {
|
||||
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder(RegistryAccess registryAccess) {
|
||||
return this.createLoaderBuilder();
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ public abstract class Configurations<G, W> {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -117,9 +117,9 @@ public abstract class Configurations<G, W> {
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
@ -7,8 +7,13 @@ import io.papermc.paper.configuration.type.number.DoubleOr;
|
|||
import io.papermc.paper.configuration.type.number.IntOr;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.component.DataComponentType;
|
||||
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.ItemStack;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
|
@ -16,10 +21,13 @@ import org.spongepowered.configurate.objectmapping.meta.Comment;
|
|||
import org.spongepowered.configurate.objectmapping.meta.PostProcess;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Required;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Setting;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.util.HashSet;
|
||||
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 {
|
||||
|
@ -354,4 +362,68 @@ public class GlobalConfiguration 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 = new Items();
|
||||
|
||||
public class Items extends ConfigurationPart {
|
||||
public static Set<DataComponentType<?>> OVERRIDEN_TYPES = new HashSet<>();
|
||||
|
||||
public boolean enableItemObfuscation = false;
|
||||
public AssetObfuscationConfiguration allModels = new AssetObfuscationConfiguration(true,
|
||||
Set.of(
|
||||
DataComponents.MAX_STACK_SIZE,
|
||||
DataComponents.MAX_DAMAGE,
|
||||
DataComponents.DAMAGE,
|
||||
DataComponents.UNBREAKABLE,
|
||||
DataComponents.CUSTOM_NAME,
|
||||
DataComponents.ITEM_NAME,
|
||||
DataComponents.LORE,
|
||||
DataComponents.RARITY,
|
||||
DataComponents.ENCHANTMENTS,
|
||||
DataComponents.ATTRIBUTE_MODIFIERS,
|
||||
DataComponents.CUSTOM_DATA,
|
||||
DataComponents.WRITABLE_BOOK_CONTENT,
|
||||
DataComponents.WRITTEN_BOOK_CONTENT,
|
||||
DataComponents.MAP_ID,
|
||||
DataComponents.MAP_DECORATIONS,
|
||||
DataComponents.MAP_POST_PROCESSING,
|
||||
DataComponents.LODESTONE_TRACKER
|
||||
)
|
||||
);
|
||||
public Map<String, AssetObfuscationConfiguration> modelOverrides = Map.of(
|
||||
net.minecraft.world.item.Items.ELYTRA.components().get(DataComponents.ITEM_MODEL).toString(), new AssetObfuscationConfiguration(
|
||||
true, Util.make(new HashSet<>(), (obj) -> {
|
||||
obj.addAll(allModels.sanitizedComponents());
|
||||
obj.remove(DataComponents.DAMAGE); // don't sanitize damage for elytra
|
||||
}
|
||||
)
|
||||
));
|
||||
|
||||
public AssetObfuscationConfiguration getAssetObfuscation(ItemStack resourceLocation) {
|
||||
return this.modelOverrides.getOrDefault(resourceLocation.get(DataComponents.ITEM_MODEL).toString(), this.allModels); // todo make optimized resourcelocation lookup
|
||||
}
|
||||
|
||||
@ConfigSerializable
|
||||
public record AssetObfuscationConfiguration(boolean sanitizeCount, Set<DataComponentType<?>> sanitizedComponents) {
|
||||
}
|
||||
|
||||
@PostProcess
|
||||
public void computeOverridenTypes() {
|
||||
OVERRIDEN_TYPES.addAll(this.allModels.sanitizedComponents);
|
||||
for (AssetObfuscationConfiguration configuration : this.modelOverrides.values()) {
|
||||
OVERRIDEN_TYPES.addAll(configuration.sanitizedComponents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ import java.util.List;
|
|||
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;
|
||||
|
@ -193,16 +194,17 @@ public class PaperConfigurations extends Configurations<GlobalConfiguration, Wor
|
|||
}
|
||||
|
||||
@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))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -316,7 +318,7 @@ public class PaperConfigurations extends Configurations<GlobalConfiguration, Wor
|
|||
|
||||
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()));
|
||||
|
@ -456,7 +458,7 @@ public class PaperConfigurations extends Configurations<GlobalConfiguration, Wor
|
|||
@VisibleForTesting
|
||||
static ConfigurationNode createForTesting() {
|
||||
ObjectMapper.Factory factory = defaultGlobalFactoryBuilder(ObjectMapper.factoryBuilder()).build();
|
||||
ConfigurationOptions options = defaultGlobalOptions(defaultOptions(ConfigurationOptions.defaults()))
|
||||
ConfigurationOptions options = defaultGlobalOptions(null, defaultOptions(ConfigurationOptions.defaults())) // TODO: ???
|
||||
.serializers(builder -> builder.register(type -> ConfigurationPart.class.isAssignableFrom(erase(type)), factory.asTypeSerializer()));
|
||||
return BasicConfigurationNode.root(options);
|
||||
}
|
||||
|
|
|
@ -88,17 +88,6 @@ public class WorldConfiguration extends ConfigurationPart {
|
|||
|
||||
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 {
|
||||
|
|
|
@ -1,19 +1,34 @@
|
|||
package io.papermc.paper.util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.UnaryOperator;
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.papermc.paper.configuration.GlobalConfiguration;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.core.component.DataComponentType;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.BundleContents;
|
||||
import net.minecraft.world.item.component.ChargedProjectiles;
|
||||
import net.minecraft.world.item.component.ItemContainerContents;
|
||||
import net.minecraft.world.item.component.LodestoneTracker;
|
||||
import net.minecraft.world.item.enchantment.ItemEnchantments;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.apache.commons.lang3.math.Fraction;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
|
@ -29,9 +44,41 @@ public final class DataSanitizationUtil {
|
|||
return sanitizer;
|
||||
}
|
||||
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, ChargedProjectiles> CHARGED_PROJECTILES = codec(ChargedProjectiles.STREAM_CODEC, DataSanitizationUtil::sanitizeChargedProjectiles);
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, BundleContents> BUNDLE_CONTENTS = codec(BundleContents.STREAM_CODEC, DataSanitizationUtil::sanitizeBundleContents);
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, ItemContainerContents> CONTAINER = codec(ItemContainerContents.STREAM_CODEC, contents -> ItemContainerContents.EMPTY);
|
||||
public static SafeAutoClosable passContext(final ItemStack itemStack) {
|
||||
final DataSanitizer sanitizer = DATA_SANITIZER.get();
|
||||
if (!sanitizer.isSanitizing()) {
|
||||
return () -> {}; // Dont pass any context
|
||||
}
|
||||
|
||||
return new ContentScope(sanitizer.scope.get(), itemStack);
|
||||
}
|
||||
|
||||
|
||||
public static final Map<DataComponentType<?>, StreamCodec<? super RegistryFriendlyByteBuf, ?>> BUILT_IN_CODEC_OVERRIDES = Map.of(
|
||||
DataComponents.CHARGED_PROJECTILES, codec(ChargedProjectiles.STREAM_CODEC, DataSanitizationUtil::sanitizeChargedProjectiles),
|
||||
DataComponents.BUNDLE_CONTENTS, codec(BundleContents.STREAM_CODEC, DataSanitizationUtil::sanitizeBundleContents),
|
||||
DataComponents.CONTAINER, codec(ItemContainerContents.STREAM_CODEC, contents -> ItemContainerContents.EMPTY)
|
||||
);
|
||||
|
||||
public static final Map<DataComponentType<?>, Object> EMPTY_MAP = Map.of(
|
||||
DataComponents.LODESTONE_TRACKER, new LodestoneTracker(Optional.empty(), false)
|
||||
);
|
||||
|
||||
|
||||
public static <T> StreamCodec<? super RegistryFriendlyByteBuf, T> get(DataComponentType<T> componentType, StreamCodec<? super RegistryFriendlyByteBuf, T> vanillaCodec) {
|
||||
// First check do we have our built in overrides, if so, override it.
|
||||
if (BUILT_IN_CODEC_OVERRIDES.containsKey(componentType)) {
|
||||
return (StreamCodec<RegistryFriendlyByteBuf, T>) BUILT_IN_CODEC_OVERRIDES.get(componentType);
|
||||
}
|
||||
|
||||
// Now check the obfuscation, where we lookup if the type is overriden in any of the configurations, if so, wrap the codec
|
||||
GlobalConfiguration.Anticheat.Obfuscation.Items obfuscation = GlobalConfiguration.get().anticheat.obfuscation.items;
|
||||
if (obfuscation.enableItemObfuscation && GlobalConfiguration.Anticheat.Obfuscation.Items.OVERRIDEN_TYPES.contains(componentType)) {
|
||||
return new DataSanitizationCodec<>(vanillaCodec, new DefaultDataComponentSanitizer<>(componentType, EMPTY_MAP.get(componentType)));
|
||||
}
|
||||
|
||||
return vanillaCodec;
|
||||
}
|
||||
|
||||
private static ChargedProjectiles sanitizeChargedProjectiles(final ChargedProjectiles projectiles) {
|
||||
if (projectiles.isEmpty()) {
|
||||
|
@ -65,11 +112,37 @@ public final class DataSanitizationUtil {
|
|||
// Ensure that potentially overstacked bundles are not represented by empty (count=0) itemstacks.
|
||||
return new BundleContents(sanitizedRepresentation);
|
||||
}
|
||||
|
||||
private static <B, A> StreamCodec<B, A> codec(final StreamCodec<B, A> delegate, final UnaryOperator<A> sanitizer) {
|
||||
return new DataSanitizationCodec<>(delegate, sanitizer);
|
||||
}
|
||||
|
||||
public static int sanitizeCount(ItemStack itemStack, int count) {
|
||||
GlobalConfiguration.Anticheat.Obfuscation.Items items = GlobalConfiguration.get().anticheat.obfuscation.items;
|
||||
if (items.enableItemObfuscation && items.getAssetObfuscation(itemStack).sanitizeCount()) {
|
||||
return 1;
|
||||
} else {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private record DefaultDataComponentSanitizer<A>(DataComponentType<?> type, @Nullable Object override) implements UnaryOperator<A> {
|
||||
|
||||
@Override
|
||||
public A apply(final A oldvalue) {
|
||||
ContentScope scope = DATA_SANITIZER.get().scope.get();
|
||||
ItemStack targetItemstack = scope.itemStack;
|
||||
// Does this asset override this component? If not, return oldValue.
|
||||
if (!GlobalConfiguration.get().anticheat.obfuscation.items.getAssetObfuscation(targetItemstack).sanitizedComponents().contains(this.type)) {
|
||||
return oldvalue;
|
||||
}
|
||||
|
||||
// We don't need to check if its overriden because we KNOW it is if we are serializing it over to the client.
|
||||
|
||||
return (A) scope.itemStack.getPrototype().getOrDefault(this.type, this.override == null ? oldvalue : this.override);
|
||||
}
|
||||
}
|
||||
|
||||
private record DataSanitizationCodec<B, A>(StreamCodec<B, A> delegate, UnaryOperator<A> sanitizer) implements StreamCodec<B, A> {
|
||||
|
||||
@Override
|
||||
|
@ -79,7 +152,7 @@ public final class DataSanitizationUtil {
|
|||
|
||||
@Override
|
||||
public void encode(final @NonNull B buf, final @NonNull A value) {
|
||||
if (!DATA_SANITIZER.get().value().get()) {
|
||||
if (!DATA_SANITIZER.get().isSanitizing()) {
|
||||
this.delegate.encode(buf, value);
|
||||
} else {
|
||||
this.delegate.encode(buf, this.sanitizer.apply(value));
|
||||
|
@ -87,10 +160,10 @@ public final class DataSanitizationUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public record DataSanitizer(AtomicBoolean value) implements AutoCloseable {
|
||||
public record DataSanitizer(AtomicBoolean value, AtomicReference<ContentScope> scope) implements SafeAutoClosable {
|
||||
|
||||
public DataSanitizer() {
|
||||
this(new AtomicBoolean(false));
|
||||
this(new AtomicBoolean(false), new AtomicReference<>());
|
||||
}
|
||||
|
||||
public void start() {
|
||||
|
@ -101,6 +174,27 @@ public final class DataSanitizationUtil {
|
|||
public void close() {
|
||||
this.value.compareAndSet(true, false);
|
||||
}
|
||||
|
||||
public boolean isSanitizing() {
|
||||
return value().get();
|
||||
}
|
||||
}
|
||||
|
||||
public record ContentScope(@Nullable ContentScope oldScope, ItemStack itemStack) implements SafeAutoClosable {
|
||||
public ContentScope {
|
||||
Preconditions.checkNotNull(DATA_SANITIZER.get(), "Expected data santizier content available");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
DATA_SANITIZER.get().scope().set(this.oldScope);
|
||||
}
|
||||
}
|
||||
|
||||
public interface SafeAutoClosable extends AutoCloseable {
|
||||
|
||||
@Override
|
||||
void close();
|
||||
}
|
||||
|
||||
private DataSanitizationUtil() {
|
||||
|
|
Loading…
Reference in a new issue