Fix NBT stripping and trimming (#2929)

This commit is contained in:
Hannes Greule 2024-09-28 21:38:52 +02:00 committed by GitHub
parent b4635e85c9
commit 3b6e5e83c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 74 additions and 56 deletions

View File

@ -512,8 +512,8 @@ public class LimitExtent extends AbstractDelegateExtent implements IBatchProcess
if (!processing) { if (!processing) {
return set; return set;
} }
int tiles = set.getTiles().size(); int tiles = set.tiles().size();
int ents = set.getEntities().size() + set.getEntityRemoves().size(); int ents = set.entities().size() + set.getEntityRemoves().size();
limit.THROW_MAX_CHANGES(tiles + ents); limit.THROW_MAX_CHANGES(tiles + ents);
limit.THROW_MAX_BLOCKSTATES(tiles); limit.THROW_MAX_BLOCKSTATES(tiles);
limit.THROW_MAX_ENTITIES(ents); limit.THROW_MAX_ENTITIES(ents);

View File

@ -2,14 +2,12 @@ package com.fastasyncworldedit.core.extent;
import com.fastasyncworldedit.core.extent.processor.ProcessorScope; import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
import com.fastasyncworldedit.core.math.BlockVector3ChunkMap; import com.fastasyncworldedit.core.math.BlockVector3ChunkMap;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.util.ExtentTraverser; import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.google.common.collect.ImmutableMap;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Entity;
@ -20,16 +18,17 @@ import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.NbtValued; import com.sk89q.worldedit.world.NbtValued;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.enginehub.linbus.tree.LinTag;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.HashMap; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class StripNBTExtent extends AbstractDelegateExtent implements IBatchProcessor { public class StripNBTExtent extends AbstractDelegateExtent implements IBatchProcessor {
@ -75,79 +74,82 @@ public class StripNBTExtent extends AbstractDelegateExtent implements IBatchProc
if (!(block instanceof BaseBlock localBlock)) { if (!(block instanceof BaseBlock localBlock)) {
return block; return block;
} }
if (!localBlock.hasNbtData()) { final LinCompoundTag nbt = localBlock.getNbt();
if (nbt == null) {
return block; return block;
} }
CompoundTag nbt = localBlock.getNbtData(); LinCompoundTag.Builder nbtBuilder = nbt.toBuilder();
Map<String, Tag<?, ?>> value = new HashMap<>(nbt.getValue());
for (String key : strip) { for (String key : strip) {
value.remove(key); nbtBuilder.remove(key);
} }
return (B) localBlock.toBaseBlock(new CompoundTag(value)); return (B) localBlock.toBaseBlock(nbtBuilder.build());
} }
public <T extends NbtValued> T stripEntityNBT(T entity) { public <T extends NbtValued> T stripEntityNBT(T entity) {
if (!entity.hasNbtData()) { LinCompoundTag nbt = entity.getNbt();
if (nbt == null) {
return entity; return entity;
} }
CompoundTag nbt = entity.getNbtData(); LinCompoundTag.Builder nbtBuilder = nbt.toBuilder();
Map<String, Tag<?, ?>> value = new HashMap<>(nbt.getValue());
for (String key : strip) { for (String key : strip) {
value.remove(key); nbtBuilder.remove(key);
} }
entity.setNbtData(new CompoundTag(value)); entity.setNbt(nbtBuilder.build());
return entity; return entity;
} }
@Override @Override
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
Map<BlockVector3, CompoundTag> tiles = set.getTiles(); Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
Set<CompoundTag> entities = set.getEntities(); Collection<FaweCompoundTag> entities = set.entities();
if (tiles.isEmpty() && entities.isEmpty()) { if (tiles.isEmpty() && entities.isEmpty()) {
return set; return set;
} }
boolean isBv3ChunkMap = tiles instanceof BlockVector3ChunkMap; boolean isBv3ChunkMap = tiles instanceof BlockVector3ChunkMap;
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) { for (final var entry : tiles.entrySet()) {
ImmutableMap.Builder<String, Tag<?, ?>> map = ImmutableMap.builder(); FaweCompoundTag original = entry.getValue();
final AtomicBoolean isStripped = new AtomicBoolean(false); FaweCompoundTag result = stripNbt(original);
entry.getValue().getValue().forEach((k, v) -> { if (original != result) {
if (strip.contains(k.toLowerCase())) {
isStripped.set(true);
} else {
map.put(k, v);
}
});
if (isStripped.get()) {
if (isBv3ChunkMap) { if (isBv3ChunkMap) {
// Replace existing value with stripped value // Replace existing value with stripped value
tiles.put(entry.getKey(), new CompoundTag(map.build())); tiles.put(entry.getKey(), result);
} else { } else {
entry.setValue(new CompoundTag(map.build())); entry.setValue(result);
} }
} }
} }
Set<CompoundTag> stripped = new HashSet<>(); Set<FaweCompoundTag> stripped = new HashSet<>();
Iterator<CompoundTag> iterator = entities.iterator(); Iterator<FaweCompoundTag> iterator = entities.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
CompoundTag entity = iterator.next(); FaweCompoundTag original = iterator.next();
ImmutableMap.Builder<String, Tag<?, ?>> map = ImmutableMap.builder(); FaweCompoundTag result = stripNbt(original);
final AtomicBoolean isStripped = new AtomicBoolean(false); if (original != result) {
entity.getValue().forEach((k, v) -> {
if (strip.contains(k.toUpperCase(Locale.ROOT))) {
isStripped.set(true);
} else {
map.put(k, v);
}
});
if (isStripped.get()) {
iterator.remove(); iterator.remove();
stripped.add(new CompoundTag(map.build())); stripped.add(result);
} }
} }
set.getEntities().addAll(stripped); // this relies on entities.addAll(...) not throwing an exception if empty+unmodifiable (=> stripped is empty too)
entities.addAll(stripped);
return set; return set;
} }
private FaweCompoundTag stripNbt(
FaweCompoundTag compoundTag
) {
LinCompoundTag.Builder builder = LinCompoundTag.builder();
boolean stripped = false;
for (var entry : compoundTag.linTag().value().entrySet()) {
String k = entry.getKey();
LinTag<?> v = entry.getValue();
if (strip.contains(k.toLowerCase(Locale.ROOT))) {
stripped = true;
} else {
builder.put(k, v);
}
}
return stripped ? FaweCompoundTag.of(builder.build()) : compoundTag;
}
@Nullable @Nullable
@Override @Override
public Extent construct(final Extent child) { public Extent construct(final Extent child) {

View File

@ -3,15 +3,16 @@ package com.fastasyncworldedit.core.queue;
import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor; import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor;
import com.fastasyncworldedit.core.extent.processor.MultiBatchProcessor; import com.fastasyncworldedit.core.extent.processor.MultiBatchProcessor;
import com.fastasyncworldedit.core.extent.processor.ProcessorScope; import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
import com.sk89q.jnbt.CompoundTag; import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.block.BlockTypesCache;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.function.Function; import java.util.function.Function;
@ -156,11 +157,11 @@ public interface IBatchProcessor {
*/ */
@Deprecated(forRemoval = true, since = "2.8.4") @Deprecated(forRemoval = true, since = "2.8.4")
default boolean trimNBT(IChunkSet set, Function<BlockVector3, Boolean> contains) { default boolean trimNBT(IChunkSet set, Function<BlockVector3, Boolean> contains) {
Set<CompoundTag> ents = set.getEntities(); Collection<FaweCompoundTag> ents = set.entities();
if (!ents.isEmpty()) { if (!ents.isEmpty()) {
ents.removeIf(ent -> !contains.apply(ent.getEntityPosition().toBlockPoint())); ents.removeIf(ent -> !contains.apply(NbtUtils.entityPosition(ent).toBlockPoint()));
} }
Map<BlockVector3, CompoundTag> tiles = set.getTiles(); Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
if (!tiles.isEmpty()) { if (!tiles.isEmpty()) {
tiles.entrySet().removeIf(blockVector3CompoundTagEntry -> !contains tiles.entrySet().removeIf(blockVector3CompoundTagEntry -> !contains
.apply(blockVector3CompoundTagEntry.getKey())); .apply(blockVector3CompoundTagEntry.getKey()));
@ -177,11 +178,11 @@ public interface IBatchProcessor {
default boolean trimNBT( default boolean trimNBT(
IChunkSet set, Function<BlockVector3, Boolean> containsEntity, Function<BlockVector3, Boolean> containsTile IChunkSet set, Function<BlockVector3, Boolean> containsEntity, Function<BlockVector3, Boolean> containsTile
) { ) {
Set<CompoundTag> ents = set.getEntities(); Collection<FaweCompoundTag> ents = set.entities();
if (!ents.isEmpty()) { if (!ents.isEmpty()) {
ents.removeIf(ent -> !containsEntity.apply(ent.getEntityPosition().toBlockPoint())); ents.removeIf(ent -> !containsEntity.apply(NbtUtils.entityPosition(ent).toBlockPoint()));
} }
Map<BlockVector3, CompoundTag> tiles = set.getTiles(); Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
if (!tiles.isEmpty()) { if (!tiles.isEmpty()) {
tiles.entrySet().removeIf(blockVector3CompoundTagEntry -> !containsTile.apply(blockVector3CompoundTagEntry.getKey())); tiles.entrySet().removeIf(blockVector3CompoundTagEntry -> !containsTile.apply(blockVector3CompoundTagEntry.getKey()));
} }

View File

@ -61,7 +61,7 @@ public interface IBlocks extends Trimable {
@Deprecated(forRemoval = true, since = "2.11.2") @Deprecated(forRemoval = true, since = "2.11.2")
default Map<BlockVector3, CompoundTag> getTiles() { default Map<BlockVector3, CompoundTag> getTiles() {
return AdaptedMap.immutable(tiles(), pos -> pos, IBlocks::toCompoundTag); return AdaptedMap.values(tiles(), ct -> FaweCompoundTag.of(ct.toLinTag()), IBlocks::toCompoundTag);
} }
Map<BlockVector3, FaweCompoundTag> tiles(); Map<BlockVector3, FaweCompoundTag> tiles();

View File

@ -2,6 +2,7 @@ package com.fastasyncworldedit.core.util;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.storage.InvalidFormatException; import com.sk89q.worldedit.world.storage.InvalidFormatException;
import org.enginehub.linbus.tree.LinByteTag; import org.enginehub.linbus.tree.LinByteTag;
@ -211,4 +212,18 @@ public final class NbtUtils {
map.put("PersistentIDLSB", LinLongTag.of(uuid.getLeastSignificantBits())); map.put("PersistentIDLSB", LinLongTag.of(uuid.getLeastSignificantBits()));
} }
/**
* {@return the position data of the given tag}
*
* @param compoundTag the tag to extract position information from
* @since TODO
*/
public static Vector3 entityPosition(FaweCompoundTag compoundTag) {
LinListTag<LinDoubleTag> pos = compoundTag.linTag().getListTag("Pos", LinTagType.doubleTag());
double x = pos.get(0).valueAsDouble();
double y = pos.get(1).valueAsDouble();
double z = pos.get(2).valueAsDouble();
return Vector3.at(x, y, z);
}
} }