mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-03-04 06:44:40 +01:00
rework ContraptionData
This commit is contained in:
parent
fe77abaf6e
commit
98698ddabd
7 changed files with 149 additions and 99 deletions
|
@ -18,13 +18,13 @@ import com.simibubi.create.AllItems;
|
|||
import com.simibubi.create.AllMovementBehaviours;
|
||||
import com.simibubi.create.AllPackets;
|
||||
import com.simibubi.create.AllSoundEvents;
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.content.contraptions.actors.psi.PortableStorageInterfaceMovement;
|
||||
import com.simibubi.create.content.contraptions.actors.seat.SeatBlock;
|
||||
import com.simibubi.create.content.contraptions.actors.seat.SeatEntity;
|
||||
import com.simibubi.create.content.contraptions.actors.trainControls.ControlsStopControllingPacket;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementBehaviour;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
import com.simibubi.create.content.contraptions.data.ContraptionSyncLimiting;
|
||||
import com.simibubi.create.content.contraptions.elevator.ElevatorContraption;
|
||||
import com.simibubi.create.content.contraptions.glue.SuperGlueEntity;
|
||||
import com.simibubi.create.content.contraptions.mounted.MountedContraption;
|
||||
|
@ -615,11 +615,8 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
|
|||
CompoundTag compound = new CompoundTag();
|
||||
writeAdditional(compound, true);
|
||||
|
||||
if (ContraptionData.isTooLargeForSync(compound)) {
|
||||
String info = getContraption().getType().id + " @" + position() + " (" + getStringUUID() + ")";
|
||||
Create.LOGGER.warn("Could not send Contraption Spawn Data (Packet too big): " + info);
|
||||
compound = null;
|
||||
}
|
||||
if (ContraptionSyncLimiting.isTooLargeForSync(compound))
|
||||
compound = null; // don't sync contraption data
|
||||
|
||||
buffer.writeNbt(compound);
|
||||
}
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
package com.simibubi.create.content.contraptions;
|
||||
|
||||
import com.simibubi.create.compat.Mods;
|
||||
import com.simibubi.create.foundation.mixin.accessor.NbtAccounterAccessor;
|
||||
import com.simibubi.create.infrastructure.config.AllConfigs;
|
||||
|
||||
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;
|
||||
|
||||
public class ContraptionData {
|
||||
/**
|
||||
* A sane, default maximum for contraption data size.
|
||||
*/
|
||||
public static final int DEFAULT_LIMIT = 2_000_000;
|
||||
/**
|
||||
* Connectivity expands the NBT packet limit to 2 GB.
|
||||
*/
|
||||
public static final int CONNECTIVITY_LIMIT = Integer.MAX_VALUE;
|
||||
/**
|
||||
* Packet Fixer expands the NBT packet limit to 200 MB.
|
||||
*/
|
||||
public static final int PACKET_FIXER_LIMIT = 209_715_200;
|
||||
/**
|
||||
* XL Packets expands the NBT packet limit to 2 GB.
|
||||
*/
|
||||
public static final int XL_PACKETS_LIMIT = 2_000_000_000;
|
||||
/**
|
||||
* Minecart item sizes are limited by the vanilla slot change packet ({@link ClientboundContainerSetSlotPacket}).
|
||||
* {@link #DEFAULT_LIMIT} is used as the default.
|
||||
* Connectivity, PacketFixer, and XL Packets expand the size limit.
|
||||
* If one of these mods is loaded, we take advantage of it and use the higher limit.
|
||||
*/
|
||||
public static final int PICKUP_LIMIT;
|
||||
|
||||
static {
|
||||
int limit = DEFAULT_LIMIT;
|
||||
|
||||
// Check from largest to smallest to use the smallest limit if multiple mods are loaded.
|
||||
// It is necessary to use the smallest limit because even if multiple mods are loaded,
|
||||
// not all of their mixins may be applied. Therefore, it is safest to only assume that
|
||||
// the mod with the smallest limit is actually active.
|
||||
if (Mods.CONNECTIVITY.isLoaded()) {
|
||||
limit = CONNECTIVITY_LIMIT;
|
||||
}
|
||||
if (Mods.XLPACKETS.isLoaded()) {
|
||||
limit = XL_PACKETS_LIMIT;
|
||||
}
|
||||
if (Mods.PACKETFIXER.isLoaded()) {
|
||||
limit = PACKET_FIXER_LIMIT;
|
||||
}
|
||||
|
||||
PICKUP_LIMIT = limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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_LIMIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package com.simibubi.create.content.contraptions.data;
|
||||
|
||||
import com.simibubi.create.compat.Mods;
|
||||
import com.simibubi.create.foundation.mixin.accessor.NbtAccounterAccessor;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtAccounter;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
public class ContraptionPickupLimiting {
|
||||
/// The default NBT limit, defined by {@link FriendlyByteBuf#readNbt()}.
|
||||
public static final int NBT_LIMIT = 2_097_152;
|
||||
|
||||
// increased nbt limits provided by other mods.
|
||||
public static final int PACKET_FIXER_LIMIT = NBT_LIMIT * 100;
|
||||
public static final int XL_PACKETS_LIMIT = Integer.MAX_VALUE;
|
||||
|
||||
// leave some space for the rest of the packet.
|
||||
public static final int BUFFER = 20_000;
|
||||
|
||||
// the actual limit to be used
|
||||
public static final int LIMIT = Util.make(() -> {
|
||||
// the smallest limit needs to be used, as we can't guarantee that all mixins are applied if multiple are present.
|
||||
if (Mods.PACKETFIXER.isLoaded()) {
|
||||
return PACKET_FIXER_LIMIT;
|
||||
} else if (Mods.XLPACKETS.isLoaded()) {
|
||||
return XL_PACKETS_LIMIT;
|
||||
}
|
||||
|
||||
// none are present, use vanilla default
|
||||
return NBT_LIMIT;
|
||||
}) - BUFFER;
|
||||
|
||||
/**
|
||||
* @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 nbtSize(data) > LIMIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the size of the given NBT when read by the client according to {@link NbtAccounter}
|
||||
*/
|
||||
private static long nbtSize(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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.simibubi.create.content.contraptions.data;
|
||||
|
||||
import com.simibubi.create.compat.Mods;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
public class ContraptionSyncLimiting {
|
||||
/**
|
||||
* Contraption entity sync is limited by the clientbound custom payload limit, since that's what Forge's
|
||||
* extended spawn packet uses. The NBT limit is irrelevant since it's bypassed on deserialization.
|
||||
*/
|
||||
public static final int SIZE_LIMIT = 1_048_576;
|
||||
|
||||
// increased packet limits provided by other mods.
|
||||
public static final int PACKET_FIXER_LIMIT = SIZE_LIMIT * 100;
|
||||
public static final int XL_PACKETS_LIMIT = Integer.MAX_VALUE;
|
||||
|
||||
// leave some room for the rest of the packet.
|
||||
public static final int BUFFER = 20_000;
|
||||
|
||||
// the actual limit to be used
|
||||
public static final int LIMIT = Util.make(() -> {
|
||||
// the smallest limit needs to be used, as we can't guarantee that all mixins are applied if multiple are present.
|
||||
if (Mods.PACKETFIXER.isLoaded()) {
|
||||
return PACKET_FIXER_LIMIT;
|
||||
} else if (Mods.XLPACKETS.isLoaded()) {
|
||||
return XL_PACKETS_LIMIT;
|
||||
}
|
||||
|
||||
// none are present, use vanilla default
|
||||
return SIZE_LIMIT;
|
||||
}) - BUFFER;
|
||||
|
||||
/**
|
||||
* @return true if the given NBT is too large for a contraption to be synced to clients.
|
||||
*/
|
||||
public static boolean isTooLargeForSync(CompoundTag data) {
|
||||
return byteSize(data) > LIMIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the size of the given NBT when encoded, in bytes
|
||||
*/
|
||||
private static long byteSize(CompoundTag data) {
|
||||
FriendlyByteBuf test = new FriendlyByteBuf(Unpooled.buffer());
|
||||
test.writeNbt(data);
|
||||
return test.writerIndex();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
# Data Limiting
|
||||
This pair of classes prevents clients from getting chunkbanned when contraptions are too large.
|
||||
|
||||
This information is up-to-date as of 1.20.1.
|
||||
|
||||
There's a few different packet limits in play:
|
||||
- the NBT limit: `2_097_152`, enforced by `FriendlyByteBuf.readNbt()`
|
||||
- the clientbound custom payload limit: `1_048_576` bytes, applies to `ClientboundCustomPayloadPacket`
|
||||
- the serverbound custom payload limit: `32767` bytes, applies to `ServerboundCustomPayloadPacket`
|
||||
- the packet limit: `8_388_608` bytes, applies to all packets
|
||||
|
||||
# NBT Size vs Bytes
|
||||
There's two units in play as well - NBT Size and Bytes. The NBT limit uses NBT size, while the other
|
||||
three use bytes. I'm (TropheusJ) not sure what exactly an NBT Size unit is - it's not bits, but it's
|
||||
close.
|
||||
|
||||
Because of this discrepancy, the NBT limit is actually much lower than it seems. It will usually be
|
||||
the first limit hit.
|
||||
|
||||
Bytes are found by writing a tag to a buffer and getting its `writerIndex`. NBT Size is found using
|
||||
an `NbtAccounter`.
|
||||
|
||||
# Sync
|
||||
Sync is pretty straightforward.
|
||||
|
||||
The only limit relevant here is the clientbound custom payload limit. The NBT limit would be relevant, but
|
||||
client-side deserialization bypasses it.
|
||||
|
||||
Sync is much less of an issue compared to pickup, since a lot of data can be skipped when syncing.
|
||||
|
||||
# Pickup
|
||||
Two limits are relevant for pickup: the NBT limit and the packet limit.
|
||||
|
||||
The NBT limit is hit way sooner, and is usually the limiting factor. Other mods may increase it, in
|
||||
which case the packet limit may become relevant.
|
||||
|
||||
The custom payload limit is not relevant since item sync goes through the vanilla `ClientboundContainerSetSlotPacket`.
|
|
@ -10,11 +10,11 @@ import com.simibubi.create.AllItems;
|
|||
import com.simibubi.create.AllMovementBehaviours;
|
||||
import com.simibubi.create.content.contraptions.AbstractContraptionEntity;
|
||||
import com.simibubi.create.content.contraptions.Contraption;
|
||||
import com.simibubi.create.content.contraptions.ContraptionData;
|
||||
import com.simibubi.create.content.contraptions.ContraptionMovementSetting;
|
||||
import com.simibubi.create.content.contraptions.OrientedContraptionEntity;
|
||||
import com.simibubi.create.content.contraptions.actors.psi.PortableStorageInterfaceMovement;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
import com.simibubi.create.content.contraptions.data.ContraptionPickupLimiting;
|
||||
import com.simibubi.create.content.kinetics.deployer.DeployerFakePlayer;
|
||||
import com.simibubi.create.foundation.advancement.AllAdvancements;
|
||||
import com.simibubi.create.foundation.utility.CreateLang;
|
||||
|
@ -249,7 +249,7 @@ public class MinecartContraptionItem extends Item {
|
|||
|
||||
ItemStack generatedStack = create(type, oce).setHoverName(entity.getCustomName());
|
||||
|
||||
if (ContraptionData.isTooLargeForPickup(generatedStack.serializeNBT())) {
|
||||
if (ContraptionPickupLimiting.isTooLargeForPickup(generatedStack.serializeNBT())) {
|
||||
MutableComponent message = CreateLang.translateDirect("contraption.minecart_contraption_too_big")
|
||||
.withStyle(ChatFormatting.RED);
|
||||
player.displayClientMessage(message, true);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.simibubi.create.infrastructure.config;
|
||||
|
||||
import com.simibubi.create.content.contraptions.ContraptionData;
|
||||
import com.simibubi.create.content.contraptions.ContraptionMovementSetting;
|
||||
|
||||
import net.createmod.catnip.config.ConfigBase;
|
||||
|
@ -32,8 +31,6 @@ public class CKinetics extends ConfigBase {
|
|||
|
||||
public final ConfigGroup contraptions = group(1, "contraptions", "Moving Contraptions");
|
||||
public final ConfigInt maxBlocksMoved = i(2048, 1, "maxBlocksMoved", Comments.maxBlocksMoved);
|
||||
public final ConfigInt maxDataSize =
|
||||
i(ContraptionData.DEFAULT_LIMIT, 0, "maxDataSize", Comments.bytes, Comments.maxDataDisable, Comments.maxDataSize, Comments.maxDataSize2);
|
||||
public final ConfigInt maxChassisRange = i(16, 1, "maxChassisRange", Comments.maxChassisRange);
|
||||
public final ConfigInt maxPistonPoles = i(64, 1, "maxPistonPoles", Comments.maxPistonPoles);
|
||||
public final ConfigInt maxRopeLength = i(384, 1, "maxRopeLength", Comments.maxRopeLength);
|
||||
|
@ -86,9 +83,6 @@ public class CKinetics extends ConfigBase {
|
|||
"multiplier used for calculating exhaustion from speed when a crank is turned.";
|
||||
static String maxBlocksMoved =
|
||||
"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 maxPistonPoles = "Maximum amount of extension poles behind a Mechanical Piston.";
|
||||
static String maxRopeLength = "Max length of rope available off a Rope Pulley.";
|
||||
|
|
Loading…
Add table
Reference in a new issue