mirror of
https://github.com/architectury/architectury-api.git
synced 2026-03-30 21:05:56 -05:00
Add support for PacketTransformer and a SplitPacketTransformer (#142)
* Add support for PacketTransformer and a SplitPacketTransformer * Add testmod and make it work * Add experimental, Remove generics, they are pointless
This commit is contained in:
@@ -20,48 +20,67 @@
|
||||
package dev.architectury.networking;
|
||||
|
||||
import dev.architectury.injectables.annotations.ExpectPlatform;
|
||||
import dev.architectury.networking.transformers.PacketCollector;
|
||||
import dev.architectury.networking.transformers.PacketSink;
|
||||
import dev.architectury.networking.transformers.PacketTransformer;
|
||||
import dev.architectury.networking.transformers.SinglePacketCollector;
|
||||
import dev.architectury.utils.Env;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class NetworkManager {
|
||||
@ExpectPlatform
|
||||
public static void registerReceiver(Side side, ResourceLocation id, NetworkReceiver receiver) {
|
||||
throw new AssertionError();
|
||||
registerReceiver(side, id, Collections.emptyList(), receiver);
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
@ApiStatus.Experimental
|
||||
public static void registerReceiver(Side side, ResourceLocation id, List<PacketTransformer> packetTransformers, NetworkReceiver receiver) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ApiStatus.ScheduledForRemoval
|
||||
public static Packet<?> toPacket(Side side, ResourceLocation id, FriendlyByteBuf buf) {
|
||||
SinglePacketCollector sink = new SinglePacketCollector(null);
|
||||
collectPackets(sink, side, id, buf);
|
||||
return sink.getPacket();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ApiStatus.ScheduledForRemoval
|
||||
public static List<Packet<?>> toPackets(Side side, ResourceLocation id, FriendlyByteBuf buf) {
|
||||
PacketCollector sink = new PacketCollector(null);
|
||||
collectPackets(sink, side, id, buf);
|
||||
return sink.collect();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static void collectPackets(PacketSink sink, Side side, ResourceLocation id, FriendlyByteBuf buf) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static void sendToPlayer(ServerPlayer player, ResourceLocation id, FriendlyByteBuf buf) {
|
||||
Objects.requireNonNull(player, "Unable to send packet to a 'null' player!").connection.send(toPacket(serverToClient(), id, buf));
|
||||
collectPackets(PacketSink.ofPlayer(player), serverToClient(), id, buf);
|
||||
}
|
||||
|
||||
public static void sendToPlayers(Iterable<ServerPlayer> players, ResourceLocation id, FriendlyByteBuf buf) {
|
||||
var packet = toPacket(serverToClient(), id, buf);
|
||||
for (var player : players) {
|
||||
Objects.requireNonNull(player, "Unable to send packet to a 'null' player!").connection.send(packet);
|
||||
}
|
||||
collectPackets(PacketSink.ofPlayers(players), serverToClient(), id, buf);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public static void sendToServer(ResourceLocation id, FriendlyByteBuf buf) {
|
||||
if (Minecraft.getInstance().getConnection() != null) {
|
||||
Minecraft.getInstance().getConnection().send(toPacket(clientToServer(), id, buf));
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to send packet to the server while not in game!");
|
||||
}
|
||||
collectPackets(PacketSink.client(), clientToServer(), id, buf);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package dev.architectury.networking.transformers;
|
||||
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class PacketCollector implements PacketSink {
|
||||
@Nullable
|
||||
private final Consumer<Packet<?>> consumer;
|
||||
private final List<Packet<?>> packets = new ArrayList<>();
|
||||
|
||||
public PacketCollector(@Nullable Consumer<Packet<?>> consumer) {
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Packet<?> packet) {
|
||||
packets.add(packet);
|
||||
if (this.consumer != null) {
|
||||
this.consumer.accept(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Packet<?>> collect() {
|
||||
return packets;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package dev.architectury.networking.transformers;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PacketSink {
|
||||
static PacketSink ofPlayer(ServerPlayer player) {
|
||||
return packet -> Objects.requireNonNull(player, "Unable to send packet to a 'null' player!").connection.send(packet);
|
||||
}
|
||||
|
||||
static PacketSink ofPlayers(Iterable<? extends ServerPlayer> players) {
|
||||
return packet -> {
|
||||
for (var player : players) {
|
||||
Objects.requireNonNull(player, "Unable to send packet to a 'null' player!").connection.send(packet);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
static PacketSink client() {
|
||||
return packet -> {
|
||||
if (Minecraft.getInstance().getConnection() != null) {
|
||||
Minecraft.getInstance().getConnection().send(packet);
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to send packet to the server while not in game!");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void accept(Packet<?> packet);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package dev.architectury.networking.transformers;
|
||||
|
||||
import dev.architectury.networking.NetworkManager;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public interface PacketTransformer {
|
||||
void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink);
|
||||
|
||||
void outbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, TransformationSink sink);
|
||||
|
||||
@FunctionalInterface
|
||||
interface TransformationSink {
|
||||
void accept(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf);
|
||||
}
|
||||
|
||||
static PacketTransformer none() {
|
||||
return new PacketTransformer() {
|
||||
@Override
|
||||
public void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink) {
|
||||
sink.accept(side, id, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void outbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, TransformationSink sink) {
|
||||
sink.accept(side, id, buf);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static PacketTransformer concat(Iterable<? extends PacketTransformer> transformers) {
|
||||
if (transformers instanceof Collection && ((Collection<? extends PacketTransformer>) transformers).isEmpty()) {
|
||||
return PacketTransformer.none();
|
||||
} else if (transformers instanceof Collection && ((Collection<? extends PacketTransformer>) transformers).size() == 1) {
|
||||
return transformers.iterator().next();
|
||||
}
|
||||
return new PacketTransformer() {
|
||||
@Override
|
||||
public void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink) {
|
||||
traverse(side, id, buf, context, sink, true, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void outbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, TransformationSink sink) {
|
||||
traverse(side, id, buf, null, sink, false, 0);
|
||||
}
|
||||
|
||||
private void traverse(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, @Nullable NetworkManager.PacketContext context, TransformationSink outerSink, boolean inbound, int index) {
|
||||
if (transformers instanceof List) {
|
||||
if (((List<? extends PacketTransformer>) transformers).size() > index) {
|
||||
PacketTransformer transformer = ((List<? extends PacketTransformer>) transformers).get(index);
|
||||
TransformationSink sink = (side1, id1, buf1) -> {
|
||||
traverse(side1, id1, buf1, context, outerSink, inbound, index + 1);
|
||||
};
|
||||
if (inbound) {
|
||||
transformer.inbound(side, id, buf, context, sink);
|
||||
} else {
|
||||
transformer.outbound(side, id, buf, sink);
|
||||
}
|
||||
} else {
|
||||
outerSink.accept(side, id, buf);
|
||||
}
|
||||
} else {
|
||||
Iterator<? extends PacketTransformer> iterator = transformers.iterator();
|
||||
for (int i = 0; i < index; i++) {
|
||||
iterator.next();
|
||||
}
|
||||
PacketTransformer transformer = iterator.hasNext() ? iterator.next() : PacketTransformer.none();
|
||||
TransformationSink sink = (side1, id1, buf1) -> {
|
||||
if (iterator.hasNext()) {
|
||||
traverse(side1, id1, buf1, context, outerSink, inbound, index + 1);
|
||||
} else {
|
||||
outerSink.accept(side1, id1, buf1);
|
||||
}
|
||||
};
|
||||
if (inbound) {
|
||||
transformer.inbound(side, id, buf, context, sink);
|
||||
} else {
|
||||
transformer.outbound(side, id, buf, sink);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package dev.architectury.networking.transformers;
|
||||
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class SinglePacketCollector implements PacketSink {
|
||||
@Nullable
|
||||
private final Consumer<Packet<?>> consumer;
|
||||
private Packet<?> packet;
|
||||
|
||||
public SinglePacketCollector(@Nullable Consumer<Packet<?>> consumer) {
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Packet<?> packet) {
|
||||
if (this.packet == null) {
|
||||
this.packet = packet;
|
||||
if (this.consumer != null) {
|
||||
this.consumer.accept(packet);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Already accepted one packet!");
|
||||
}
|
||||
}
|
||||
|
||||
public Packet<?> getPacket() {
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package dev.architectury.networking.transformers;
|
||||
|
||||
import dev.architectury.event.events.client.ClientPlayerEvent;
|
||||
import dev.architectury.event.events.common.PlayerEvent;
|
||||
import dev.architectury.networking.NetworkManager;
|
||||
import dev.architectury.utils.Env;
|
||||
import dev.architectury.utils.EnvExecutor;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public class SplitPacketTransformer implements PacketTransformer {
|
||||
private static final Logger LOGGER = LogManager.getLogger(SplitPacketTransformer.class);
|
||||
private static final byte START = 0x0;
|
||||
private static final byte PART = 0x1;
|
||||
private static final byte END = 0x2;
|
||||
private static final byte ONLY = 0x3;
|
||||
|
||||
private static class PartKey {
|
||||
private final NetworkManager.Side side;
|
||||
@Nullable
|
||||
private final UUID playerUUID;
|
||||
|
||||
public PartKey(NetworkManager.Side side, @Nullable UUID playerUUID) {
|
||||
this.side = side;
|
||||
this.playerUUID = playerUUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof PartKey)) return false;
|
||||
PartKey key = (PartKey) o;
|
||||
return side == key.side && Objects.equals(playerUUID, key.playerUUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(side, playerUUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PartKey{" +
|
||||
"side=" + side +
|
||||
", playerUUID=" + playerUUID +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
private static class PartData {
|
||||
private final ResourceLocation id;
|
||||
private final int partsExpected;
|
||||
private final List<FriendlyByteBuf> parts;
|
||||
|
||||
public PartData(ResourceLocation id, int partsExpected) {
|
||||
this.id = id;
|
||||
this.partsExpected = partsExpected;
|
||||
this.parts = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<PartKey, PartData> cache = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
public SplitPacketTransformer() {
|
||||
PlayerEvent.PLAYER_QUIT.register(player -> {
|
||||
cache.keySet().removeIf(key -> Objects.equals(key.playerUUID, player.getUUID()));
|
||||
});
|
||||
EnvExecutor.runInEnv(Env.CLIENT, () -> new Client()::init);
|
||||
}
|
||||
|
||||
private class Client {
|
||||
@Environment(EnvType.CLIENT)
|
||||
private void init() {
|
||||
ClientPlayerEvent.CLIENT_PLAYER_QUIT.register(player -> {
|
||||
cache.keySet().removeIf(key -> key.side == NetworkManager.Side.S2C);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink) {
|
||||
PartKey key = side == NetworkManager.Side.S2C ? new PartKey(side, null) : new PartKey(side, context.getPlayer().getUUID());
|
||||
PartData data;
|
||||
switch (buf.readByte()) {
|
||||
case START:
|
||||
data = new PartData(id, buf.readInt());
|
||||
if (cache.put(key, data) != null) {
|
||||
LOGGER.warn("Received invalid START packet for SplitPacketTransformer with packet id " + id + " for side " + side);
|
||||
}
|
||||
buf.retain();
|
||||
data.parts.add(buf);
|
||||
break;
|
||||
case PART:
|
||||
if ((data = cache.get(key)) == null) {
|
||||
LOGGER.warn("Received invalid PART packet for SplitPacketTransformer with packet id " + id + " for side " + side);
|
||||
buf.release();
|
||||
} else if (!data.id.equals(id)) {
|
||||
LOGGER.warn("Received invalid PART packet for SplitPacketTransformer with packet id " + id + " for side " + side + ", id in cache is " + data.id);
|
||||
buf.release();
|
||||
for (FriendlyByteBuf part : data.parts) {
|
||||
if (part != buf) {
|
||||
part.release();
|
||||
}
|
||||
}
|
||||
cache.remove(key);
|
||||
} else {
|
||||
buf.retain();
|
||||
data.parts.add(buf);
|
||||
}
|
||||
break;
|
||||
case END:
|
||||
if ((data = cache.get(key)) == null) {
|
||||
LOGGER.warn("Received invalid END packet for SplitPacketTransformer with packet id " + id + " for side " + side);
|
||||
buf.release();
|
||||
} else if (!data.id.equals(id)) {
|
||||
LOGGER.warn("Received invalid END packet for SplitPacketTransformer with packet id " + id + " for side " + side + ", id in cache is " + data.id);
|
||||
buf.release();
|
||||
for (FriendlyByteBuf part : data.parts) {
|
||||
if (part != buf) {
|
||||
part.release();
|
||||
}
|
||||
}
|
||||
cache.remove(key);
|
||||
} else {
|
||||
buf.retain();
|
||||
data.parts.add(buf);
|
||||
}
|
||||
if (data.parts.size() != data.partsExpected) {
|
||||
LOGGER.warn("Received invalid END packet for SplitPacketTransformer with packet id " + id + " for side " + side + " with size " + data.parts + ", parts expected is " + data.partsExpected);
|
||||
for (FriendlyByteBuf part : data.parts) {
|
||||
if (part != buf) {
|
||||
part.release();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FriendlyByteBuf byteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(data.parts.toArray(new ByteBuf[0])));
|
||||
sink.accept(side, data.id, byteBuf);
|
||||
byteBuf.release();
|
||||
}
|
||||
cache.remove(key);
|
||||
break;
|
||||
case ONLY:
|
||||
sink.accept(side, id, buf);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Illegal split packet header!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void outbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, TransformationSink sink) {
|
||||
int maxSize = (side == NetworkManager.Side.C2S ? 32767 : 1048576) - 1 - 10;
|
||||
if (buf.readableBytes() <= maxSize) {
|
||||
ByteBuf stateBuf = Unpooled.buffer(1);
|
||||
stateBuf.writeByte(ONLY);
|
||||
FriendlyByteBuf packetBuffer = new FriendlyByteBuf(Unpooled.wrappedBuffer(stateBuf, buf));
|
||||
sink.accept(side, id, packetBuffer);
|
||||
} else {
|
||||
int partSize = maxSize - 4;
|
||||
int parts = Math.round(buf.readableBytes() / (float) partSize);
|
||||
for (int i = 0; i < parts; i++) {
|
||||
FriendlyByteBuf packetBuffer = new FriendlyByteBuf(Unpooled.buffer());
|
||||
if (i == 0) {
|
||||
packetBuffer.writeByte(START);
|
||||
packetBuffer.writeInt(parts);
|
||||
} else if (i == parts - 1) {
|
||||
packetBuffer.writeByte(END);
|
||||
} else {
|
||||
packetBuffer.writeByte(PART);
|
||||
}
|
||||
int next = Math.min(buf.readableBytes(), partSize);
|
||||
packetBuffer.writeBytes(buf.retainedSlice(buf.readerIndex(), next));
|
||||
buf.skipBytes(next);
|
||||
sink.accept(side, id, packetBuffer);
|
||||
}
|
||||
|
||||
}
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user