Add a MessageType for network messages

Everything old is new again!

CC's network message implementation has gone through several iterations:

 - Originally network messages were implemented with a single class,
   which held an packet id/type and and opaque blobs of data (as
   string/int/byte/NBT arrays), and a big switch statement to decode and
   process this data.

 - In 42d3901ee3, we split the messages
   into different classes all inheriting from NetworkMessage - this bit
   we've stuck with ever since.

   Each packet had a `getId(): int` method, which returned the
   discriminator for this packet.

 - However, getId() was only used when registering the packet, not when
   sending, and so in ce0685c31f we
   removed it, just passing in a constant integer at registration
   instead.

 - In 53abe5e56e, we made some relatively
   minor changes to make the code more multi-loader/split-source
   friendly. However, this meant when we finally came to add Fabric
   support (8152f19b6e), we had to
   re-implement a lot of Forge's network code.

In 1.20.4, Forge moves to a system much closer to Fabric's (and indeed,
Minecraft's own CustomPacketPayload), and so it makes sense to adapt to
that now. As such, we:

 - Add a new MessageType interface. This is implemented by the
   loader-specific modules, and holds whatever information is needed to
   register the packet (e.g. discriminator, reader function).

 - Each NetworkMessage now has a type(): MessageType<?> function. This
   is used by the Fabric networking code (and for NeoForge's on 1.20.4)
   instead of a class lookup.

 - NetworkMessages now creates/stores these MessageType<T>s (much like
   we'd do for registries), and provides getters for the
   clientbound/serverbound messages. Mod initialisers then call these
   getters to register packets.

 - For Forge, this is relatively unchanged. For Fabric, we now
   `FabricPacket`s.
This commit is contained in:
Jonathan Coates 2024-01-03 09:15:38 +00:00
parent ed3a17f9b9
commit 234f69e8e5
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
33 changed files with 370 additions and 185 deletions

View File

@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.network;
/**
* A type of message to send over the network.
* <p>
* Much like recipe or argument serialisers, each type of {@link NetworkMessage} should have a unique type associated
* with it. This holds platform-specific information about how the packet should be sent over the network.
*
* @param <T> The type of message to send
* @see NetworkMessages
* @see NetworkMessage#type()
*/
public interface MessageType<T extends NetworkMessage<?>> {
}

View File

@ -17,6 +17,13 @@
* @see ServerNetworkContext
*/
public interface NetworkMessage<T> {
/**
* Get the type of this message.
*
* @return The type of this message.
*/
MessageType<?> type();
/**
* Write this packet to a buffer.
* <p>
@ -24,7 +31,7 @@ public interface NetworkMessage<T> {
*
* @param buf The buffer to write data to.
*/
void toBytes(FriendlyByteBuf buf);
void write(FriendlyByteBuf buf);
/**
* Handle this {@link NetworkMessage}.

View File

@ -4,48 +4,84 @@
package dan200.computercraft.shared.network;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.network.client.*;
import dan200.computercraft.shared.network.server.*;
import dan200.computercraft.shared.platform.PlatformHelper;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import java.util.function.Function;
import java.util.*;
/**
* Registry for all packets provided by CC: Tweaked.
* List of all {@link MessageType}s provided by CC: Tweaked.
*
* @see PlatformHelper The platform helper is used to send packets.
*/
public final class NetworkMessages {
private static final IntSet seenIds = new IntOpenHashSet();
private static final Set<String> seenChannel = new HashSet<>();
private static final List<MessageType<? extends NetworkMessage<ServerNetworkContext>>> serverMessages = new ArrayList<>();
private static final List<MessageType<? extends NetworkMessage<ClientNetworkContext>>> clientMessages = new ArrayList<>();
public static final MessageType<ComputerActionServerMessage> COMPUTER_ACTION = registerServerbound(0, "computer_action", ComputerActionServerMessage.class, ComputerActionServerMessage::new);
public static final MessageType<QueueEventServerMessage> QUEUE_EVENT = registerServerbound(1, "queue_event", QueueEventServerMessage.class, QueueEventServerMessage::new);
public static final MessageType<KeyEventServerMessage> KEY_EVENT = registerServerbound(2, "key_event", KeyEventServerMessage.class, KeyEventServerMessage::new);
public static final MessageType<MouseEventServerMessage> MOUSE_EVENT = registerServerbound(3, "mouse_event", MouseEventServerMessage.class, MouseEventServerMessage::new);
public static final MessageType<UploadFileMessage> UPLOAD_FILE = registerServerbound(4, "upload_file", UploadFileMessage.class, UploadFileMessage::new);
public static final MessageType<ChatTableClientMessage> CHAT_TABLE = registerClientbound(10, "chat_table", ChatTableClientMessage.class, ChatTableClientMessage::new);
public static final MessageType<PocketComputerDataMessage> POCKET_COMPUTER_DATA = registerClientbound(11, "pocket_computer_data", PocketComputerDataMessage.class, PocketComputerDataMessage::new);
public static final MessageType<PocketComputerDeletedClientMessage> POCKET_COMPUTER_DELETED = registerClientbound(12, "pocket_computer_deleted", PocketComputerDeletedClientMessage.class, PocketComputerDeletedClientMessage::new);
public static final MessageType<ComputerTerminalClientMessage> COMPUTER_TERMINAL = registerClientbound(13, "computer_terminal", ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new);
public static final MessageType<PlayRecordClientMessage> PLAY_RECORD = registerClientbound(14, "play_record", PlayRecordClientMessage.class, PlayRecordClientMessage::new);
public static final MessageType<MonitorClientMessage> MONITOR_CLIENT = registerClientbound(15, "monitor_client", MonitorClientMessage.class, MonitorClientMessage::new);
public static final MessageType<SpeakerAudioClientMessage> SPEAKER_AUDIO = registerClientbound(16, "speaker_audio", SpeakerAudioClientMessage.class, SpeakerAudioClientMessage::new);
public static final MessageType<SpeakerMoveClientMessage> SPEAKER_MOVE = registerClientbound(17, "speaker_move", SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new);
public static final MessageType<SpeakerPlayClientMessage> SPEAKER_PLAY = registerClientbound(18, "speaker_play", SpeakerPlayClientMessage.class, SpeakerPlayClientMessage::new);
public static final MessageType<SpeakerStopClientMessage> SPEAKER_STOP = registerClientbound(19, "speaker_stop", SpeakerStopClientMessage.class, SpeakerStopClientMessage::new);
public static final MessageType<UploadResultMessage> UPLOAD_RESULT = registerClientbound(20, "upload_result", UploadResultMessage.class, UploadResultMessage::new);
public static final MessageType<UpgradesLoadedMessage> UPGRADES_LOADED = registerClientbound(21, "upgrades_loaded", UpgradesLoadedMessage.class, UpgradesLoadedMessage::new);
private NetworkMessages() {
}
public interface PacketRegistry {
<T extends NetworkMessage<ClientNetworkContext>> void registerClientbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder);
<T extends NetworkMessage<ServerNetworkContext>> void registerServerbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder);
private static <C, T extends NetworkMessage<C>> MessageType<T> register(
List<MessageType<? extends NetworkMessage<C>>> messages,
int id, String channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader
) {
if (!seenIds.add(id)) throw new IllegalArgumentException("Duplicate id " + id);
if (!seenChannel.add(channel)) throw new IllegalArgumentException("Duplicate channel " + channel);
var type = PlatformHelper.get().createMessageType(id, new ResourceLocation(ComputerCraftAPI.MOD_ID, channel), klass, reader);
messages.add(type);
return type;
}
public static void register(PacketRegistry registry) {
// Server messages
registry.registerServerbound(0, ComputerActionServerMessage.class, ComputerActionServerMessage::new);
registry.registerServerbound(1, QueueEventServerMessage.class, QueueEventServerMessage::new);
registry.registerServerbound(2, KeyEventServerMessage.class, KeyEventServerMessage::new);
registry.registerServerbound(3, MouseEventServerMessage.class, MouseEventServerMessage::new);
registry.registerServerbound(4, UploadFileMessage.class, UploadFileMessage::new);
private static <T extends NetworkMessage<ServerNetworkContext>> MessageType<T> registerServerbound(int id, String channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
return register(serverMessages, id, channel, klass, reader);
}
// Client messages
registry.registerClientbound(10, ChatTableClientMessage.class, ChatTableClientMessage::new);
registry.registerClientbound(11, PocketComputerDataMessage.class, PocketComputerDataMessage::new);
registry.registerClientbound(12, PocketComputerDeletedClientMessage.class, PocketComputerDeletedClientMessage::new);
registry.registerClientbound(13, ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new);
registry.registerClientbound(14, PlayRecordClientMessage.class, PlayRecordClientMessage::new);
registry.registerClientbound(15, MonitorClientMessage.class, MonitorClientMessage::new);
registry.registerClientbound(16, SpeakerAudioClientMessage.class, SpeakerAudioClientMessage::new);
registry.registerClientbound(17, SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new);
registry.registerClientbound(18, SpeakerPlayClientMessage.class, SpeakerPlayClientMessage::new);
registry.registerClientbound(19, SpeakerStopClientMessage.class, SpeakerStopClientMessage::new);
registry.registerClientbound(20, UploadResultMessage.class, UploadResultMessage::new);
registry.registerClientbound(21, UpgradesLoadedMessage.class, UpgradesLoadedMessage::new);
private static <T extends NetworkMessage<ClientNetworkContext>> MessageType<T> registerClientbound(int id, String channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
return register(clientMessages, id, channel, klass, reader);
}
/**
* Get all serverbound message types.
*
* @return An unmodifiable sequence of all serverbound message types.
*/
public static Collection<MessageType<? extends NetworkMessage<ServerNetworkContext>>> getServerbound() {
return Collections.unmodifiableCollection(serverMessages);
}
/**
* Get all clientbound message types.
*
* @return An unmodifiable sequence of all clientbound message types.
*/
public static Collection<MessageType<? extends NetworkMessage<ClientNetworkContext>>> getClientbound() {
return Collections.unmodifiableCollection(clientMessages);
}
}

View File

@ -5,7 +5,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.command.text.TableBuilder;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
@ -43,7 +45,7 @@ public ChatTableClientMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeUtf(table.getId(), MAX_LEN);
buf.writeVarInt(table.getColumns());
buf.writeBoolean(table.getHeaders() != null);
@ -63,4 +65,9 @@ public void toBytes(FriendlyByteBuf buf) {
public void handle(ClientNetworkContext context) {
context.handleChatTable(table);
}
@Override
public MessageType<ChatTableClientMessage> type() {
return NetworkMessages.CHAT_TABLE;
}
}

View File

@ -5,7 +5,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@ -25,7 +27,7 @@ public ComputerTerminalClientMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(containerId);
terminal.write(buf);
}
@ -34,4 +36,9 @@ public void toBytes(FriendlyByteBuf buf) {
public void handle(ClientNetworkContext context) {
context.handleComputerTerminal(containerId, terminal);
}
@Override
public MessageType<ComputerTerminalClientMessage> type() {
return NetworkMessages.COMPUTER_TERMINAL;
}
}

View File

@ -5,7 +5,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
@ -25,7 +27,7 @@ public MonitorClientMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeBlockPos(pos);
state.write(buf);
}
@ -34,4 +36,9 @@ public void toBytes(FriendlyByteBuf buf) {
public void handle(ClientNetworkContext context) {
context.handleMonitorData(pos, state);
}
@Override
public MessageType<MonitorClientMessage> type() {
return NetworkMessages.MONITOR_CLIENT;
}
}

View File

@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
@ -43,7 +45,7 @@ public PlayRecordClientMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeBlockPos(pos);
buf.writeNullable(soundEvent, (b, e) -> e.writeToNetwork(b));
buf.writeNullable(name, FriendlyByteBuf::writeUtf);
@ -53,4 +55,9 @@ public void toBytes(FriendlyByteBuf buf) {
public void handle(ClientNetworkContext context) {
context.handlePlayRecord(pos, soundEvent, name);
}
@Override
public MessageType<PlayRecordClientMessage> type() {
return NetworkMessages.PLAY_RECORD;
}
}

View File

@ -7,7 +7,9 @@
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
import net.minecraft.network.FriendlyByteBuf;
@ -35,7 +37,7 @@ public PocketComputerDataMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(instanceId);
buf.writeEnum(state);
buf.writeVarInt(lightState);
@ -46,4 +48,9 @@ public void toBytes(FriendlyByteBuf buf) {
public void handle(ClientNetworkContext context) {
context.handlePocketComputerData(instanceId, state, lightState, terminal);
}
@Override
public MessageType<PocketComputerDataMessage> type() {
return NetworkMessages.POCKET_COMPUTER_DATA;
}
}

View File

@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
@ -20,7 +22,7 @@ public PocketComputerDeletedClientMessage(FriendlyByteBuf buffer) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(instanceId);
}
@ -28,4 +30,9 @@ public void toBytes(FriendlyByteBuf buf) {
public void handle(ClientNetworkContext context) {
context.handlePocketComputerDeleted(instanceId);
}
@Override
public MessageType<PocketComputerDeletedClientMessage> type() {
return NetworkMessages.POCKET_COMPUTER_DELETED;
}
}

View File

@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.network.FriendlyByteBuf;
@ -47,7 +49,7 @@ public SpeakerAudioClientMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeUUID(source);
pos.write(buf);
buf.writeFloat(volume);
@ -58,4 +60,9 @@ public void toBytes(FriendlyByteBuf buf) {
public void handle(ClientNetworkContext context) {
context.handleSpeakerAudio(source, pos, volume);
}
@Override
public MessageType<SpeakerAudioClientMessage> type() {
return NetworkMessages.SPEAKER_AUDIO;
}
}

View File

@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.network.FriendlyByteBuf;
@ -33,7 +35,7 @@ public SpeakerMoveClientMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeUUID(source);
pos.write(buf);
}
@ -42,4 +44,9 @@ public void toBytes(FriendlyByteBuf buf) {
public void handle(ClientNetworkContext context) {
context.handleSpeakerMove(source, pos);
}
@Override
public MessageType<SpeakerMoveClientMessage> type() {
return NetworkMessages.SPEAKER_MOVE;
}
}

View File

@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.network.FriendlyByteBuf;
@ -43,7 +45,7 @@ public SpeakerPlayClientMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeUUID(source);
pos.write(buf);
buf.writeResourceLocation(sound);
@ -55,4 +57,9 @@ public void toBytes(FriendlyByteBuf buf) {
public void handle(ClientNetworkContext context) {
context.handleSpeakerPlay(source, pos, sound, volume, pitch);
}
@Override
public MessageType<SpeakerPlayClientMessage> type() {
return NetworkMessages.SPEAKER_PLAY;
}
}

View File

@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
import net.minecraft.network.FriendlyByteBuf;
@ -29,7 +31,7 @@ public SpeakerStopClientMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeUUID(source);
}
@ -37,4 +39,9 @@ public void toBytes(FriendlyByteBuf buf) {
public void handle(ClientNetworkContext context) {
context.handleSpeakerStop(source);
}
@Override
public MessageType<SpeakerStopClientMessage> type() {
return NetworkMessages.SPEAKER_STOP;
}
}

View File

@ -13,7 +13,9 @@
import dan200.computercraft.impl.PocketUpgrades;
import dan200.computercraft.impl.TurtleUpgrades;
import dan200.computercraft.impl.UpgradeManager;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.platform.PlatformHelper;
import net.minecraft.core.Registry;
import net.minecraft.network.FriendlyByteBuf;
@ -27,7 +29,7 @@
/**
* Syncs turtle and pocket upgrades to the client.
*/
public class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContext> {
public final class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContext> {
private final Map<String, UpgradeManager.UpgradeWrapper<TurtleUpgradeSerialiser<?>, ITurtleUpgrade>> turtleUpgrades;
private final Map<String, UpgradeManager.UpgradeWrapper<PocketUpgradeSerialiser<?>, IPocketUpgrade>> pocketUpgrades;
@ -65,7 +67,7 @@ private <R extends UpgradeSerialiser<? extends T>, T extends UpgradeBase> Map<St
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
toBytes(buf, TurtleUpgradeSerialiser.registryId(), turtleUpgrades);
toBytes(buf, PocketUpgradeSerialiser.registryId(), pocketUpgrades);
}
@ -95,4 +97,9 @@ public void handle(ClientNetworkContext context) {
TurtleUpgrades.instance().loadFromNetwork(turtleUpgrades);
PocketUpgrades.instance().loadFromNetwork(pocketUpgrades);
}
@Override
public MessageType<UpgradesLoadedMessage> type() {
return NetworkMessages.UPGRADES_LOADED;
}
}

View File

@ -6,7 +6,9 @@
import dan200.computercraft.core.util.Nullability;
import dan200.computercraft.shared.computer.upload.UploadResult;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.world.inventory.AbstractContainerMenu;
@ -43,7 +45,7 @@ public UploadResultMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(containerId);
buf.writeEnum(result);
if (result == UploadResult.ERROR) buf.writeComponent(Nullability.assertNonNull(errorMessage));
@ -53,4 +55,9 @@ public void toBytes(FriendlyByteBuf buf) {
public void handle(ClientNetworkContext context) {
context.handleUploadResult(containerId, result, errorMessage);
}
@Override
public MessageType<UploadResultMessage> type() {
return NetworkMessages.UPLOAD_RESULT;
}
}

View File

@ -5,6 +5,8 @@
package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@ -23,8 +25,8 @@ public ComputerActionServerMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
super.toBytes(buf);
public void write(FriendlyByteBuf buf) {
super.write(buf);
buf.writeEnum(action);
}
@ -37,6 +39,11 @@ protected void handle(ServerNetworkContext context, ComputerMenu container) {
}
}
@Override
public MessageType<ComputerActionServerMessage> type() {
return NetworkMessages.COMPUTER_ACTION;
}
public enum Action {
TURN_ON,
SHUTDOWN,

View File

@ -28,7 +28,7 @@ public ComputerServerMessage(FriendlyByteBuf buffer) {
@Override
@OverridingMethodsMustInvokeSuper
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(containerId);
}

View File

@ -5,6 +5,8 @@
package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@ -30,8 +32,8 @@ public KeyEventServerMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
super.toBytes(buf);
public void write(FriendlyByteBuf buf) {
super.write(buf);
buf.writeByte(type);
buf.writeVarInt(key);
}
@ -45,4 +47,9 @@ protected void handle(ServerNetworkContext context, ComputerMenu container) {
input.keyDown(key, type == TYPE_REPEAT);
}
}
@Override
public MessageType<KeyEventServerMessage> type() {
return NetworkMessages.KEY_EVENT;
}
}

View File

@ -5,6 +5,8 @@
package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@ -37,8 +39,8 @@ public MouseEventServerMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
super.toBytes(buf);
public void write(FriendlyByteBuf buf) {
super.write(buf);
buf.writeByte(type);
buf.writeVarInt(arg);
buf.writeVarInt(x);
@ -55,4 +57,9 @@ protected void handle(ServerNetworkContext context, ComputerMenu container) {
case TYPE_SCROLL -> input.mouseScroll(arg, x, y);
}
}
@Override
public MessageType<MouseEventServerMessage> type() {
return NetworkMessages.MOUSE_EVENT;
}
}

View File

@ -7,6 +7,8 @@
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.menu.ServerInputHandler;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@ -37,8 +39,8 @@ public QueueEventServerMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
super.toBytes(buf);
public void write(FriendlyByteBuf buf) {
super.write(buf);
buf.writeUtf(event);
buf.writeNbt(args == null ? null : NBTUtil.encodeObjects(args));
}
@ -47,4 +49,9 @@ public void toBytes(FriendlyByteBuf buf) {
protected void handle(ServerNetworkContext context, ComputerMenu container) {
container.getInput().queueEvent(event, args);
}
@Override
public MessageType<QueueEventServerMessage> type() {
return NetworkMessages.QUEUE_EVENT;
}
}

View File

@ -9,6 +9,8 @@
import dan200.computercraft.shared.computer.upload.FileSlice;
import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessages;
import io.netty.handler.codec.DecoderException;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@ -91,8 +93,8 @@ public UploadFileMessage(FriendlyByteBuf buf) {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
super.toBytes(buf);
public void write(FriendlyByteBuf buf) {
super.write(buf);
buf.writeUUID(uuid);
buf.writeByte(flag);
@ -166,4 +168,9 @@ protected void handle(ServerNetworkContext context, ComputerMenu container) {
input.continueUpload(uuid, slices);
if ((flag & FLAG_LAST) != 0) input.finishUpload(player, uuid);
}
@Override
public MessageType<UploadFileMessage> type() {
return NetworkMessages.UPLOAD_FILE;
}
}

View File

@ -10,6 +10,7 @@
import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.container.ContainerData;
@ -164,6 +165,18 @@ static PlatformHelper get() {
*/
void openMenu(Player player, MenuProvider owner, ContainerData menu);
/**
* Create a new {@link MessageType}.
*
* @param id The descriminator for this message type.
* @param channel The channel name for this message type.
* @param klass The type of this message.
* @param reader The function which reads the packet from a buffer. Should be the inverse to {@link NetworkMessage#write(FriendlyByteBuf)}.
* @param <T> The type of this message.
* @return The new {@link MessageType} instance.
*/
<T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader);
/**
* Send a message to a specific player.
*

View File

@ -13,6 +13,7 @@
import dan200.computercraft.impl.AbstractComputerCraftAPI;
import dan200.computercraft.impl.ComputerCraftAPIService;
import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.container.ContainerData;
@ -168,6 +169,13 @@ public void openMenu(Player player, MenuProvider owner, ContainerData menu) {
throw new UnsupportedOperationException("Cannot open menu inside tests");
}
@Override
public <T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
record TypeImpl<T extends NetworkMessage<?>>(Function<FriendlyByteBuf, T> reader) implements MessageType<T> {
}
return new TypeImpl<>(reader);
}
@Override
public ComponentAccess<IPeripheral> createPeripheralAccess(Consumer<Direction> invalidate) {
throw new UnsupportedOperationException("Cannot interact with the world inside tests");

View File

@ -29,7 +29,7 @@ class PlayRecordClientMessageTest {
@Property
public void testRoundTrip(@ForAll("message") PlayRecordClientMessage message) {
var buffer = new FriendlyByteBuf(Unpooled.directBuffer());
message.toBytes(buffer);
message.write(buffer);
var converted = new PlayRecordClientMessage(buffer);
assertEquals(buffer.readableBytes(), 0, "Whole packet was read");

View File

@ -63,7 +63,7 @@ private static List<UploadFileMessage> send(List<FileUpload> uploads) {
private static List<UploadFileMessage> roundtripPackets(List<UploadFileMessage> packets) {
return packets.stream().map(packet -> {
var buffer = new FriendlyByteBuf(Unpooled.directBuffer());
packet.toBytes(buffer);
packet.write(buffer);
// We include things like file size in the packet, but not in the count, so grant a slightly larger threshold.
assertThat("Packet is too large", buffer.writerIndex(), lessThanOrEqualTo(MAX_PACKET_SIZE + 128));
if ((packet.flag & FLAG_LAST) == 0) {

View File

@ -8,10 +8,11 @@
import dan200.computercraft.client.model.CustomModelLoader;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock;
import dan200.computercraft.shared.platform.FabricConfigFile;
import dan200.computercraft.shared.platform.NetworkHandler;
import dan200.computercraft.shared.platform.FabricMessageType;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.model.loading.v1.PreparableModelLoadingPlugin;
@ -30,10 +31,11 @@
public class ComputerCraftClient {
public static void init() {
ClientPlayNetworking.registerGlobalReceiver(NetworkHandler.ID, (client, handler, buf, responseSender) -> {
var packet = NetworkHandler.decodeClient(buf);
if (packet != null) client.execute(() -> packet.handle(ClientNetworkContext.get()));
});
for (var type : NetworkMessages.getClientbound()) {
ClientPlayNetworking.registerGlobalReceiver(
FabricMessageType.toFabricType(type), (packet, player, responseSender) -> packet.payload().handle(ClientNetworkContext.get())
);
}
ClientRegistry.register();
ClientRegistry.registerItemColours(ColorProviderRegistry.ITEM::register);

View File

@ -10,9 +10,9 @@
import dan200.computercraft.client.render.ModelRenderer;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.server.ServerNetworkContext;
import dan200.computercraft.shared.platform.NetworkHandler;
import dan200.computercraft.shared.platform.FabricMessageType;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.entity.ItemRenderer;
@ -29,7 +29,7 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper {
@Override
public void sendToServer(NetworkMessage<ServerNetworkContext> message) {
Minecraft.getInstance().player.connection.send(NetworkHandler.encodeServer(message));
ClientPlayNetworking.send(FabricMessageType.toFabricPacket(message));
}
@Override

View File

@ -13,6 +13,7 @@
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.details.FluidDetails;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.network.client.UpgradesLoadedMessage;
import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral;
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
@ -20,7 +21,7 @@
import dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullBlockEntity;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlockEntity;
import dan200.computercraft.shared.platform.FabricConfigFile;
import dan200.computercraft.shared.platform.NetworkHandler;
import dan200.computercraft.shared.platform.FabricMessageType;
import dan200.computercraft.shared.platform.PlatformHelper;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
@ -28,6 +29,7 @@
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
import net.minecraft.resources.ResourceLocation;
@ -45,7 +47,12 @@ public class ComputerCraft {
private static final LevelResource SERVERCONFIG = new LevelResource("serverconfig");
public static void init() {
NetworkHandler.init();
for (var type : NetworkMessages.getServerbound()) {
ServerPlayNetworking.registerGlobalReceiver(
FabricMessageType.toFabricType(type), (packet, player, sender) -> packet.payload().handle(() -> player)
);
}
ModRegistry.register();
ModRegistry.registerMainThread();

View File

@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.platform;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
import net.fabricmc.fabric.api.networking.v1.PacketType;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import java.util.function.Function;
/**
* An implementation of {@link MessageType} for Fabric.
* <p>
* This provides conversions between the {@link FabricPacket}/{@link PacketType} and {@link NetworkMessage}/{@link MessageType}
* interfaces, allowing us to interop between the two.
*
* @param type The underlying {@link PacketType}
* @param <T> The type of the message.
*/
public record FabricMessageType<T extends NetworkMessage<?>>(
PacketType<PacketWrapper<T>> type
) implements MessageType<T> {
public FabricMessageType(ResourceLocation id, Function<FriendlyByteBuf, T> reader) {
this(PacketType.create(id, b -> new PacketWrapper<>(reader.apply(b))));
}
public static <T extends NetworkMessage<?>> PacketType<PacketWrapper<T>> toFabricType(MessageType<T> type) {
return ((FabricMessageType<T>) type).type();
}
public static FabricPacket toFabricPacket(NetworkMessage<?> message) {
return new PacketWrapper<>(message);
}
public record PacketWrapper<T extends NetworkMessage<?>>(T payload) implements FabricPacket {
@Override
public void write(FriendlyByteBuf buf) {
payload().write(buf);
}
@Override
public PacketType<?> getType() {
return FabricMessageType.toFabricType(payload().type());
}
}
}

View File

@ -1,94 +0,0 @@
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.platform;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.server.ServerNetworkContext;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.function.Function;
public final class NetworkHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(NetworkHandler.class);
public static final ResourceLocation ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "main");
private static final Int2ObjectMap<Function<FriendlyByteBuf, ? extends NetworkMessage<ClientNetworkContext>>> clientPackets = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<Function<FriendlyByteBuf, ? extends NetworkMessage<ServerNetworkContext>>> serverPackets = new Int2ObjectOpenHashMap<>();
private static final Object2IntMap<Class<? extends NetworkMessage<?>>> packetIds = new Object2IntOpenHashMap<>();
public static void init() {
ServerPlayNetworking.registerGlobalReceiver(ID, (server, player, handler, buf, responseSender) -> {
var packet = decodeServer(buf);
if (packet != null) server.execute(() -> packet.handle(handler::getPlayer));
});
NetworkMessages.register(new NetworkMessages.PacketRegistry() {
@Override
public <T extends NetworkMessage<ClientNetworkContext>> void registerClientbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder) {
clientPackets.put(id, decoder);
packetIds.put(type, id);
}
@Override
public <T extends NetworkMessage<ServerNetworkContext>> void registerServerbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder) {
serverPackets.put(id, decoder);
packetIds.put(type, id);
}
});
}
private static FriendlyByteBuf encode(NetworkMessage<?> message) {
var buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeByte(packetIds.getInt(message.getClass()));
message.toBytes(buf);
return buf;
}
public static ClientboundCustomPayloadPacket encodeClient(NetworkMessage<ClientNetworkContext> message) {
return new ClientboundCustomPayloadPacket(ID, encode(message));
}
public static ServerboundCustomPayloadPacket encodeServer(NetworkMessage<ServerNetworkContext> message) {
return new ServerboundCustomPayloadPacket(ID, encode(message));
}
@Nullable
private static <T> NetworkMessage<T> decode(Int2ObjectMap<Function<FriendlyByteBuf, ? extends NetworkMessage<T>>> packets, FriendlyByteBuf buffer) {
int type = buffer.readByte();
var reader = packets.get(type);
if (reader == null) {
LOGGER.debug("Unknown packet {}", type);
return null;
}
return reader.apply(buffer);
}
@Nullable
public static NetworkMessage<ServerNetworkContext> decodeServer(FriendlyByteBuf buffer) {
return decode(serverPackets, buffer);
}
@Nullable
public static NetworkMessage<ClientNetworkContext> decodeClient(FriendlyByteBuf buffer) {
return decode(clientPackets, buffer);
}
}

View File

@ -17,6 +17,7 @@
import dan200.computercraft.impl.Peripherals;
import dan200.computercraft.mixin.ArgumentTypeInfosAccessor;
import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.container.ContainerData;
@ -27,6 +28,8 @@
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache;
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.fabricmc.fabric.api.registry.FuelRegistry;
import net.fabricmc.fabric.api.resource.conditions.v1.DefaultResourceConditions;
@ -44,6 +47,8 @@
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
@ -170,31 +175,42 @@ public void openMenu(Player player, MenuProvider owner, ContainerData menu) {
player.openMenu(new WrappedMenuProvider(owner, menu));
}
@Override
public <T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
return new FabricMessageType<>(channel, reader);
}
private Packet<ClientGamePacketListener> encodeClientbound(NetworkMessage<ClientNetworkContext> message) {
var buf = PacketByteBufs.create();
message.write(buf);
return ServerPlayNetworking.createS2CPacket(FabricMessageType.toFabricType(message.type()).getId(), buf);
}
@Override
public void sendToPlayer(NetworkMessage<ClientNetworkContext> message, ServerPlayer player) {
player.connection.send(NetworkHandler.encodeClient(message));
player.connection.send(encodeClientbound(message));
}
@Override
public void sendToPlayers(NetworkMessage<ClientNetworkContext> message, Collection<ServerPlayer> players) {
if (players.isEmpty()) return;
var packet = NetworkHandler.encodeClient(message);
var packet = encodeClientbound(message);
for (var player : players) player.connection.send(packet);
}
@Override
public void sendToAllPlayers(NetworkMessage<ClientNetworkContext> message, MinecraftServer server) {
server.getPlayerList().broadcastAll(NetworkHandler.encodeClient(message));
server.getPlayerList().broadcastAll(encodeClientbound(message));
}
@Override
public void sendToAllAround(NetworkMessage<ClientNetworkContext> message, ServerLevel level, Vec3 pos, float distance) {
level.getServer().getPlayerList().broadcast(null, pos.x, pos.y, pos.z, distance, level.dimension(), NetworkHandler.encodeClient(message));
level.getServer().getPlayerList().broadcast(null, pos.x, pos.y, pos.z, distance, level.dimension(), encodeClientbound(message));
}
@Override
public void sendToAllTracking(NetworkMessage<ClientNetworkContext> message, LevelChunk chunk) {
var packet = NetworkHandler.encodeClient(message);
var packet = encodeClientbound(message);
for (var player : ((ServerChunkCache) chunk.getLevel().getChunkSource()).chunkMap.getPlayers(chunk.getPos(), false)) {
player.connection.send(packet);
}

View File

@ -5,12 +5,11 @@
package dan200.computercraft.shared.platform;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.server.ServerNetworkContext;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
@ -47,20 +46,15 @@ private NetworkHandler() {
}
public static void setup() {
IntSet usedIds = new IntOpenHashSet();
NetworkMessages.register(new NetworkMessages.PacketRegistry() {
@Override
public <T extends NetworkMessage<ClientNetworkContext>> void registerClientbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder) {
if (!usedIds.add(id)) throw new IllegalArgumentException("Already have a packet with id " + id);
registerMainThread(id, NetworkDirection.PLAY_TO_CLIENT, type, decoder, x -> ClientNetworkContext.get());
}
for (var type : NetworkMessages.getServerbound()) {
var forgeType = (MessageTypeImpl<? extends NetworkMessage<ServerNetworkContext>>) type;
registerMainThread(forgeType, NetworkDirection.PLAY_TO_SERVER, c -> () -> assertNonNull(c.getSender()));
}
@Override
public <T extends NetworkMessage<ServerNetworkContext>> void registerServerbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder) {
if (!usedIds.add(id)) throw new IllegalArgumentException("Already have a packet with id " + id);
registerMainThread(id, NetworkDirection.PLAY_TO_SERVER, type, decoder, c -> () -> assertNonNull(c.getSender()));
}
});
for (var type : NetworkMessages.getClientbound()) {
var forgeType = (MessageTypeImpl<? extends NetworkMessage<ClientNetworkContext>>) type;
registerMainThread(forgeType, NetworkDirection.PLAY_TO_CLIENT, x -> ClientNetworkContext.get());
}
}
static void sendToPlayer(NetworkMessage<ClientNetworkContext> packet, ServerPlayer player) {
@ -96,19 +90,16 @@ public static void sendToServer(NetworkMessage<ServerNetworkContext> packet) {
*
* @param <T> The type of the packet to send.
* @param <H> The context this packet is evaluated under.
* @param type The class of the type of packet to send.
* @param id The identifier for this packet type.
* @param type The message type to register.
* @param direction A network direction which will be asserted before any processing of this message occurs
* @param decoder The factory for this type of packet.
* @param handler Gets or constructs the handler for this packet.
*/
static <H, T extends NetworkMessage<H>> void registerMainThread(
int id, NetworkDirection direction, Class<T> type, Function<FriendlyByteBuf, T> decoder,
Function<NetworkEvent.Context, H> handler
MessageTypeImpl<T> type, NetworkDirection direction, Function<NetworkEvent.Context, H> handler
) {
network.messageBuilder(type, id, direction)
.encoder(NetworkMessage::toBytes)
.decoder(decoder)
network.messageBuilder(type.klass(), type.id(), direction)
.encoder(NetworkMessage::write)
.decoder(type.reader())
.consumerMainThread((packet, contextSup) -> {
try {
packet.handle(handler.apply(contextSup.get()));
@ -119,4 +110,9 @@ static <H, T extends NetworkMessage<H>> void registerMainThread(
})
.add();
}
public record MessageTypeImpl<T extends NetworkMessage<?>>(
int id, Class<T> klass, Function<FriendlyByteBuf, T> reader
) implements MessageType<T> {
}
}

View File

@ -15,6 +15,7 @@
import dan200.computercraft.impl.Peripherals;
import dan200.computercraft.shared.Capabilities;
import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.container.ContainerData;
@ -157,6 +158,11 @@ public void openMenu(Player player, MenuProvider owner, ContainerData menu) {
NetworkHooks.openScreen((ServerPlayer) player, owner, menu::toBytes);
}
@Override
public <T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
return new NetworkHandler.MessageTypeImpl<>(id, klass, reader);
}
@Override
public void sendToPlayer(NetworkMessage<ClientNetworkContext> message, ServerPlayer player) {
NetworkHandler.sendToPlayer(message, player);