Skip to content

Commit b8aeec4

Browse files
Allow registering large payloads with deferred max packet size (#5119)
* port from 1.21.11 * change some names and improve impl * refactor test * compare against padded size * tests: update max packet size on both sides separately * throw if packet type is not registered for getMaxPacketSize() * change to registerLarge overload
1 parent 3c1b63d commit b8aeec4

File tree

6 files changed

+78
-23
lines changed

6 files changed

+78
-23
lines changed

fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/api/networking/v1/PayloadTypeRegistry.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package net.fabricmc.fabric.api.networking.v1;
1818

19+
import java.util.function.IntSupplier;
20+
1921
import org.jetbrains.annotations.ApiStatus;
2022

2123
import net.minecraft.network.FriendlyByteBuf;
@@ -50,7 +52,7 @@ public interface PayloadTypeRegistry<B extends FriendlyByteBuf> {
5052
* and <strong>before registering a packet handler</strong>.
5153
*
5254
* <p>Payload types registered with this method will be split into multiple packets,
53-
* allowing to send packets larger than vanilla limited size.
55+
* allowing to send packets larger than the vanilla limited size.
5456
*
5557
* @param type the payload type
5658
* @param codec the codec for the payload type
@@ -60,6 +62,27 @@ public interface PayloadTypeRegistry<B extends FriendlyByteBuf> {
6062
*/
6163
<T extends CustomPacketPayload> CustomPacketPayload.TypeAndCodec<? super B, T> registerLarge(CustomPacketPayload.Type<T> type, StreamCodec<? super B, T> codec, int maxPacketSize);
6264

65+
/**
66+
* Registers a large custom payload type.
67+
*
68+
* <p>This must be done on both the sending and receiving side, usually during mod initialization
69+
* and <strong>before registering a packet handler</strong>.
70+
*
71+
* <p>Payload types registered with this method will be split into multiple packets,
72+
* allowing to send packets larger than the vanilla limited size.
73+
*
74+
* <p>The {@code maxPacketSizeSupplier} will be called once, right before the first packet of this payload type
75+
* is sent/received on either side. This allows mods some leeway particularly during mod initialization to
76+
* dynamically determine a suitable max size.
77+
*
78+
* @param type the payload type
79+
* @param codec the codec for the payload type
80+
* @param maxPacketSizeSupplier the function that returns the max size of payload packet
81+
* @param <T> the payload type
82+
* @return the registered payload type
83+
*/
84+
<T extends CustomPacketPayload> CustomPacketPayload.TypeAndCodec<? super B, T> registerLarge(CustomPacketPayload.Type<T> type, StreamCodec<? super B, T> codec, IntSupplier maxPacketSizeSupplier);
85+
6386
/**
6487
* @return the {@link PayloadTypeRegistry} instance for the serverbound (client to server) configuration channel.
6588
*/

fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/PayloadTypeRegistryImpl.java

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
import java.util.HashMap;
2020
import java.util.Map;
2121
import java.util.Objects;
22+
import java.util.function.IntSupplier;
2223

2324
import io.netty.buffer.ByteBufUtil;
2425
import it.unimi.dsi.fastutil.objects.Object2IntMap;
2526
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
27+
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
28+
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
2629
import org.jspecify.annotations.Nullable;
2730

2831
import net.minecraft.network.ConnectionProtocol;
@@ -44,7 +47,8 @@ public class PayloadTypeRegistryImpl<B extends FriendlyByteBuf> implements Paylo
4447
public static final PayloadTypeRegistryImpl<RegistryFriendlyByteBuf> SERVERBOUND_PLAY = new PayloadTypeRegistryImpl<>(ConnectionProtocol.PLAY, PacketFlow.SERVERBOUND);
4548
public static final PayloadTypeRegistryImpl<RegistryFriendlyByteBuf> CLIENTBOUND_PLAY = new PayloadTypeRegistryImpl<>(ConnectionProtocol.PLAY, PacketFlow.CLIENTBOUND);
4649
private final Map<Identifier, CustomPacketPayload.TypeAndCodec<B, ? extends CustomPacketPayload>> packetTypes = new HashMap<>();
47-
private final Object2IntMap<Identifier> maxPacketSize = new Object2IntOpenHashMap<>();
50+
private final Object2IntMap<Identifier> maxPacketSizes = new Object2IntOpenHashMap<>();
51+
private final Object2ObjectMap<Identifier, IntSupplier> pendingMaxPacketSizes = new Object2ObjectOpenHashMap<>();
4852
private final ConnectionProtocol protocol;
4953
private final PacketFlow flow;
5054
private final int minimalSplittableSize;
@@ -66,7 +70,7 @@ public static PayloadTypeRegistryImpl<?> get(ProtocolInfo<?> state) {
6670

6771
@Override
6872
public <T extends CustomPacketPayload> CustomPacketPayload.TypeAndCodec<? super B, T> register(CustomPacketPayload.Type<T> type, StreamCodec<? super B, T> codec) {
69-
Objects.requireNonNull(type, "id");
73+
Objects.requireNonNull(type, "type");
7074
Objects.requireNonNull(codec, "codec");
7175

7276
final CustomPacketPayload.TypeAndCodec<B, T> payloadType = new CustomPacketPayload.TypeAndCodec<>(type, codec.cast());
@@ -80,15 +84,30 @@ public <T extends CustomPacketPayload> CustomPacketPayload.TypeAndCodec<? super
8084
}
8185

8286
@Override
83-
public <T extends CustomPacketPayload> CustomPacketPayload.TypeAndCodec<? super B, T> registerLarge(CustomPacketPayload.Type<T> type, StreamCodec<? super B, T> codec, int maxPayloadSize) {
84-
if (maxPayloadSize < 0) {
85-
throw new IllegalArgumentException("Provided maxPayloadSize needs to be positive!");
87+
public <T extends CustomPacketPayload> CustomPacketPayload.TypeAndCodec<? super B, T> registerLarge(CustomPacketPayload.Type<T> type, StreamCodec<? super B, T> codec, int maxPacketSize) {
88+
if (maxPacketSize < 0) {
89+
throw new IllegalArgumentException("Provided maxPacketSize needs to be positive!");
8690
}
8791

8892
CustomPacketPayload.TypeAndCodec<? super B, T> typeAndCodec = register(type, codec);
93+
padAndSetMaxPacketSize(type.id(), maxPacketSize);
94+
return typeAndCodec;
95+
}
96+
97+
@Override
98+
public <T extends CustomPacketPayload> CustomPacketPayload.TypeAndCodec<? super B, T> registerLarge(CustomPacketPayload.Type<T> type, StreamCodec<? super B, T> codec, IntSupplier maxPacketSizeSupplier) {
99+
Objects.requireNonNull(maxPacketSizeSupplier, "maxPacketSizeSupplier");
100+
101+
CustomPacketPayload.TypeAndCodec<? super B, T> typeAndCodec = register(type, codec);
102+
pendingMaxPacketSizes.put(type.id(), maxPacketSizeSupplier);
103+
return typeAndCodec;
104+
}
105+
106+
private void padAndSetMaxPacketSize(Identifier id, int maxSize) {
89107
// Defines max packet size, increased by length of packet's Identifier to cover full size of CustomPayloadX2YPackets.
90-
int identifierSize = ByteBufUtil.utf8MaxBytes(type.id().toString());
91-
int maxPacketSize = maxPayloadSize + VarInt.getByteSize(identifierSize) + identifierSize + 5 * 2;
108+
int identifierSize = ByteBufUtil.utf8MaxBytes(id.toString());
109+
int paddingSize = VarInt.getByteSize(identifierSize) + identifierSize + 5 * 2;
110+
int maxPacketSize = maxSize + paddingSize;
92111

93112
// Prevent overflow
94113
if (maxPacketSize < 0) {
@@ -97,23 +116,36 @@ public <T extends CustomPacketPayload> CustomPacketPayload.TypeAndCodec<? super
97116

98117
// No need to enable splitting, if packet's max size is smaller than chunk
99118
if (maxPacketSize > this.minimalSplittableSize) {
100-
this.maxPacketSize.put(type.id(), maxPacketSize);
119+
this.maxPacketSizes.put(id, maxPacketSize);
101120
}
102-
103-
return typeAndCodec;
104121
}
105122

106123
public CustomPacketPayload.@Nullable TypeAndCodec<B, ? extends CustomPacketPayload> get(Identifier id) {
107124
return packetTypes.get(id);
108125
}
109126

110-
public <T extends CustomPacketPayload> CustomPacketPayload.@Nullable TypeAndCodec<B, T> get(CustomPacketPayload.Type<T> id) {
127+
public <T extends CustomPacketPayload> CustomPacketPayload.@Nullable TypeAndCodec<B, T> get(CustomPacketPayload.Type<T> type) {
111128
//noinspection unchecked
112-
return (CustomPacketPayload.TypeAndCodec<B, T>) packetTypes.get(id.id());
129+
return (CustomPacketPayload.TypeAndCodec<B, T>) packetTypes.get(type.id());
113130
}
114131

115-
public int getMaxPacketSize(Identifier id) {
116-
return this.maxPacketSize.getOrDefault(id, -1);
132+
/**
133+
* @return the max packet size, or -1 if the payload type does not need splitting.
134+
*/
135+
public int getMaxPacketSizeForSplitting(Identifier id) {
136+
IntSupplier supplier = this.pendingMaxPacketSizes.remove(id);
137+
138+
if (supplier != null) {
139+
int maxPacketSize = supplier.getAsInt();
140+
141+
if (maxPacketSize < 0) {
142+
throw new IllegalArgumentException("maxPacketSize supplier for packet type " + id + ": must be positive!");
143+
}
144+
145+
padAndSetMaxPacketSize(id, maxPacketSize);
146+
}
147+
148+
return this.maxPacketSizes.getOrDefault(id, -1);
117149
}
118150

119151
public ConnectionProtocol getProtocol() {

fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/splitter/FabricPacketMerger.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ protected void decode(ChannelHandlerContext channelHandlerContext, Packet<?> pac
8282
Identifier payloadId = Identifier.STREAM_CODEC.decode(payload.byteBuf());
8383

8484
buf.readerIndex(readerIndex);
85-
int maxSize = payloadTypeRegistry.getMaxPacketSize(payloadId);
85+
int maxSize = payloadTypeRegistry.getMaxPacketSizeForSplitting(payloadId);
8686

8787
if (maxSize == -1) {
8888
throw new DecoderException("Received '" + payloadId + "' packet doesn't support splitting, but received split data!");

fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/mixin/networking/ClientboundCustomPayloadPacketMixin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ private static StreamCodec<FriendlyByteBuf, CustomPacketPayload> wrapConfigCodec
7979

8080
@Override
8181
public void fabric_split(PayloadTypeRegistryImpl<?> payloadTypeRegistry, ChannelHandlerContext channelHandlerContext, PacketEncoder<?> encoder, Packet<?> packet, Consumer<Packet<?>> consumer) throws Exception {
82-
int size = payloadTypeRegistry.getMaxPacketSize(this.payload.type().id());
82+
int size = payloadTypeRegistry.getMaxPacketSizeForSplitting(this.payload.type().id());
8383

8484
if (size == -1) {
8585
consumer.accept((Packet<?>) this);

fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/mixin/networking/ServerboundCustomPayloadPacketMixin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ private static StreamCodec<FriendlyByteBuf, CustomPacketPayload> wrapCodec(Custo
7070

7171
@Override
7272
public void fabric_split(PayloadTypeRegistryImpl<?> payloadTypeRegistry, ChannelHandlerContext channelHandlerContext, PacketEncoder<?> encoder, Packet<?> packet, Consumer<Packet<?>> consumer) throws Exception {
73-
int size = payloadTypeRegistry.getMaxPacketSize(this.payload.type().id());
73+
int size = payloadTypeRegistry.getMaxPacketSizeForSplitting(this.payload.type().id());
7474

7575
if (size == -1) {
7676
consumer.accept((Packet<?>) this);

fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/splitter/NetworkingSplitterTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
import net.fabricmc.fabric.test.networking.NetworkingTestmods;
3535

3636
public class NetworkingSplitterTest implements ModInitializer {
37-
private static final Logger LOGGER = LoggerFactory.getLogger(NetworkingSplitterTest.class);
37+
public static final Logger LOGGER = LoggerFactory.getLogger(NetworkingSplitterTest.class);
3838

39-
private static final int DATA_SIZE = 20 * 1024 * 1024;
39+
public static final int DATA_SIZE = 20 * 1024 * 1024;
4040

41-
// 20 MB of random data source
41+
// 20 MiB of random data source
4242
private static final int[][] RANDOM_DATA = {
4343
IntStream.generate(RandomSource.create(24534)::nextInt).limit(20).toArray(),
4444
IntStream.generate(RandomSource.create(24533)::nextInt).limit(DATA_SIZE / 4).toArray()
@@ -48,7 +48,7 @@ public class NetworkingSplitterTest implements ModInitializer {
4848
public void onInitialize() {
4949
// Register the payload on both sides for play and configuration
5050
PayloadTypeRegistry.clientboundPlay().registerLarge(LargePayload.TYPE, LargePayload.CODEC, DATA_SIZE + 14);
51-
PayloadTypeRegistry.serverboundPlay().registerLarge(LargePayload.TYPE, LargePayload.CODEC, DATA_SIZE + 14);
51+
PayloadTypeRegistry.serverboundPlay().registerLarge(LargePayload.TYPE, LargePayload.CODEC, () -> DATA_SIZE + 14);
5252

5353
// When the client joins, send a packet expecting it to be validated and echoed back
5454
ServerPlayConnectionEvents.JOIN.register((listener, sender, server) -> sender.sendPacket(new LargePayload(0, RANDOM_DATA[0])));
@@ -62,7 +62,7 @@ public void onInitialize() {
6262

6363
public static void validateLargePacketData(int index, int[] data, String side) {
6464
if (Arrays.equals(RANDOM_DATA[index], data)) {
65-
NetworkingTestmods.LOGGER.info("Successfully received large packet [" + index + "] on " + side);
65+
LOGGER.info("Successfully received large packet [{}] on {}", index, side);
6666
return;
6767
}
6868

0 commit comments

Comments
 (0)