Merge pull request #4213 from TropheusJ/mc1.18/contraption-data

Fix and improve handling of large contraption data
This commit is contained in:
simibubi 2023-01-14 13:53:59 +01:00 committed by GitHub
commit 318bac3704
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 105 additions and 45 deletions

View file

@ -14,7 +14,8 @@ public enum Mods {
DYNAMICTREES, DYNAMICTREES,
TCONSTRUCT, TCONSTRUCT,
CURIOS, CURIOS,
STORAGEDRAWERS; STORAGEDRAWERS,
XLPACKETS;
/** /**
* @return a boolean of whether the mod is loaded or not based on mod id * @return a boolean of whether the mod is loaded or not based on mod id

View file

@ -1,6 +1,5 @@
package com.simibubi.create.content.contraptions.components.structureMovement; package com.simibubi.create.content.contraptions.components.structureMovement;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
@ -11,11 +10,11 @@ import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.simibubi.create.foundation.utility.ContraptionData;
import org.apache.commons.lang3.mutable.MutableInt; import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.MutablePair;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllItems; import com.simibubi.create.AllItems;
import com.simibubi.create.AllMovementBehaviours; import com.simibubi.create.AllMovementBehaviours;
@ -44,7 +43,6 @@ import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
@ -598,22 +596,10 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
CompoundTag compound = new CompoundTag(); CompoundTag compound = new CompoundTag();
writeAdditional(compound, true); writeAdditional(compound, true);
try { if (ContraptionData.isTooLargeForSync(compound)) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput(); String info = getContraption().getType().id + " @" + position() + " (" + getStringUUID() + ")";
NbtIo.write(compound, dataOutput); Create.LOGGER.warn("Could not send Contraption Spawn Data (Packet too big): " + info);
byte[] byteArray = dataOutput.toByteArray(); compound = null;
int estimatedPacketSize = byteArray.length;
if (estimatedPacketSize > 2_000_000) {
Create.LOGGER.warn("Could not send Contraption Spawn Data (Packet too big): "
+ getContraption().getType().id + " @" + position() + " (" + getUUID().toString() + ")");
buffer.writeNbt(new CompoundTag());
return;
}
} catch (IOException e) {
e.printStackTrace();
buffer.writeNbt(new CompoundTag());
return;
} }
buffer.writeNbt(compound); buffer.writeNbt(compound);
@ -633,7 +619,10 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
@Override @Override
public void readSpawnData(FriendlyByteBuf additionalData) { public void readSpawnData(FriendlyByteBuf additionalData) {
readAdditional(additionalData.readNbt(), true); CompoundTag nbt = additionalData.readAnySizeNbt();
if (nbt != null) {
readAdditional(nbt, true);
}
} }
@Override @Override

View file

@ -1,14 +1,15 @@
package com.simibubi.create.content.contraptions.components.structureMovement.mounted; package com.simibubi.create.content.contraptions.components.structureMovement.mounted;
import java.io.IOException;
import java.util.List; import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.simibubi.create.foundation.utility.ContraptionData;
import net.minecraft.network.chat.MutableComponent;
import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.MutablePair;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.simibubi.create.AllItems; import com.simibubi.create.AllItems;
import com.simibubi.create.AllMovementBehaviours; import com.simibubi.create.AllMovementBehaviours;
import com.simibubi.create.content.contraptions.components.actors.PortableStorageInterfaceMovement; import com.simibubi.create.content.contraptions.components.actors.PortableStorageInterfaceMovement;
@ -31,7 +32,6 @@ import net.minecraft.core.NonNullList;
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior; import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.core.dispenser.DispenseItemBehavior; import net.minecraft.core.dispenser.DispenseItemBehavior;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.tags.BlockTags; import net.minecraft.tags.BlockTags;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
@ -251,18 +251,10 @@ public class MinecartContraptionItem extends Item {
ItemStack generatedStack = create(type, oce).setHoverName(entity.getCustomName()); ItemStack generatedStack = create(type, oce).setHoverName(entity.getCustomName());
try { if (ContraptionData.isTooLargeForPickup(generatedStack.serializeNBT())) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput(); MutableComponent message = Lang.translateDirect("contraption.minecart_contraption_too_big")
NbtIo.write(generatedStack.serializeNBT(), dataOutput); .withStyle(ChatFormatting.RED);
int estimatedPacketSize = dataOutput.toByteArray().length; player.displayClientMessage(message, true);
if (estimatedPacketSize > 2_000_000) {
player.displayClientMessage(Lang.translateDirect("contraption.minecart_contraption_too_big")
.withStyle(ChatFormatting.RED), true);
return;
}
} catch (IOException e) {
e.printStackTrace();
return; return;
} }

View file

@ -64,6 +64,7 @@ public abstract class ContraptionRenderingWorld<C extends ContraptionRenderInfo>
.map(Reference::get) .map(Reference::get)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(AbstractContraptionEntity::getContraption) .map(AbstractContraptionEntity::getContraption)
.filter(Objects::nonNull) // contraptions that are too large will not be synced, and un-synced contraptions will be null
.forEach(this::getRenderInfo); .forEach(this::getRenderInfo);
} }

View file

@ -1,6 +1,7 @@
package com.simibubi.create.foundation.config; package com.simibubi.create.foundation.config;
import com.simibubi.create.foundation.config.ui.ConfigAnnotations; import com.simibubi.create.foundation.config.ui.ConfigAnnotations;
import com.simibubi.create.foundation.utility.ContraptionData;
public class CKinetics extends ConfigBase { public class CKinetics extends ConfigBase {
@ -30,6 +31,8 @@ public class CKinetics extends ConfigBase {
public final ConfigGroup contraptions = group(1, "contraptions", "Moving Contraptions"); public final ConfigGroup contraptions = group(1, "contraptions", "Moving Contraptions");
public final ConfigInt maxBlocksMoved = i(2048, 1, "maxBlocksMoved", Comments.maxBlocksMoved); public final ConfigInt maxBlocksMoved = i(2048, 1, "maxBlocksMoved", Comments.maxBlocksMoved);
public final ConfigInt maxDataSize =
i(ContraptionData.DEFAULT_MAX, 0, "maxDataSize", Comments.bytes, Comments.maxDataDisable, Comments.maxDataSize, Comments.maxDataSize2);
public final ConfigInt maxChassisRange = i(16, 1, "maxChassisRange", Comments.maxChassisRange); public final ConfigInt maxChassisRange = i(16, 1, "maxChassisRange", Comments.maxChassisRange);
public final ConfigInt maxPistonPoles = i(64, 1, "maxPistonPoles", Comments.maxPistonPoles); public final ConfigInt maxPistonPoles = i(64, 1, "maxPistonPoles", Comments.maxPistonPoles);
public final ConfigInt maxRopeLength = i(256, 1, "maxRopeLength", Comments.maxRopeLength); public final ConfigInt maxRopeLength = i(256, 1, "maxRopeLength", Comments.maxRopeLength);
@ -75,6 +78,9 @@ public class CKinetics extends ConfigBase {
"multiplier used for calculating exhaustion from speed when a crank is turned."; "multiplier used for calculating exhaustion from speed when a crank is turned.";
static String maxBlocksMoved = static String maxBlocksMoved =
"Maximum amount of blocks in a structure movable by Pistons, Bearings or other means."; "Maximum amount of blocks in a structure movable by Pistons, Bearings or other means.";
static String maxDataSize = "Maximum amount of data a contraption can have before it can't be synced with players.";
static String maxDataSize2 = "Un-synced contraptions will not be visible and will not have collision.";
static String maxDataDisable = "[0 to disable this limit]";
static String maxChassisRange = "Maximum value of a chassis attachment range."; static String maxChassisRange = "Maximum value of a chassis attachment range.";
static String maxPistonPoles = "Maximum amount of extension poles behind a Mechanical Piston."; static String maxPistonPoles = "Maximum amount of extension poles behind a Mechanical Piston.";
static String maxRopeLength = "Max length of rope available off a Rope Pulley."; static String maxRopeLength = "Max length of rope available off a Rope Pulley.";
@ -86,6 +92,7 @@ public class CKinetics extends ConfigBase {
static String stats = "Configure speed/capacity levels for requirements and indicators."; static String stats = "Configure speed/capacity levels for requirements and indicators.";
static String rpm = "[in Revolutions per Minute]"; static String rpm = "[in Revolutions per Minute]";
static String su = "[in Stress Units]"; static String su = "[in Stress Units]";
static String bytes = "[in Bytes]";
static String mediumSpeed = "Minimum speed of rotation to be considered 'medium'"; static String mediumSpeed = "Minimum speed of rotation to be considered 'medium'";
static String fastSpeed = "Minimum speed of rotation to be considered 'fast'"; static String fastSpeed = "Minimum speed of rotation to be considered 'fast'";
static String mediumStressImpact = "Minimum stress impact to be considered 'medium'"; static String mediumStressImpact = "Minimum stress impact to be considered 'medium'";

View file

@ -0,0 +1,12 @@
package com.simibubi.create.foundation.mixin.accessor;
import net.minecraft.nbt.NbtAccounter;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(NbtAccounter.class)
public interface NbtAccounterAccessor {
@Accessor("usage")
long create$getUsage();
}

View file

@ -0,0 +1,57 @@
package com.simibubi.create.foundation.utility;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.mixin.accessor.NbtAccounterAccessor;
import io.netty.buffer.Unpooled;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraftforge.fml.ModList;
public class ContraptionData {
/**
* A sane, default maximum for contraption data size.
*/
public static final int DEFAULT_MAX = 2_000_000;
/**
* XL Packets expands the NBT packet limit to 2 GB.
*/
public static final int EXPANDED_MAX = 2_000_000_000;
/**
* Minecart item sizes are limited by the vanilla slot change packet ({@link ClientboundContainerSetSlotPacket}).
* {@link ContraptionData#DEFAULT_MAX} is used as the default.
* XL Packets expands the size limit to ~2 GB. If the mod is loaded, we take advantage of it and use the higher limit.
*/
public static final int PICKUP_MAX = Mods.XLPACKETS.isLoaded() ? EXPANDED_MAX : DEFAULT_MAX;
/**
* @return true if the given NBT is too large for a contraption to be synced to clients.
*/
public static boolean isTooLargeForSync(CompoundTag data) {
int max = AllConfigs.SERVER.kinetics.maxDataSize.get();
return max != 0 && packetSize(data) > max;
}
/**
* @return true if the given NBT is too large for a contraption to be picked up with a wrench.
*/
public static boolean isTooLargeForPickup(CompoundTag data) {
return packetSize(data) > PICKUP_MAX;
}
/**
* @return the size of the given NBT when put through a packet, in bytes.
*/
public static long packetSize(CompoundTag data) {
FriendlyByteBuf test = new FriendlyByteBuf(Unpooled.buffer());
test.writeNbt(data);
NbtAccounter sizeTracker = new NbtAccounter(Long.MAX_VALUE);
test.readNbt(sizeTracker);
long size = ((NbtAccounterAccessor) sizeTracker).create$getUsage();
test.release();
return size;
}
}

View file

@ -13,6 +13,7 @@
"accessor.DispenserBlockAccessor", "accessor.DispenserBlockAccessor",
"accessor.FallingBlockEntityAccessor", "accessor.FallingBlockEntityAccessor",
"accessor.LivingEntityAccessor", "accessor.LivingEntityAccessor",
"accessor.NbtAccounterAccessor",
"accessor.ServerLevelAccessor" "accessor.ServerLevelAccessor"
], ],
"client": [ "client": [