diff --git a/build.gradle b/build.gradle index f1af4069..87de1485 100644 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,12 @@ allprojects { options.release = 16 } + javadoc { + // Architectury's common javadoc has references to platform code, which cannot be resolved normally. + // Let's just skip the errors! + failOnError = false + } + license { header = rootProject.file("HEADER") diff --git a/common/src/main/java/dev/architectury/networking/simple/BaseC2SMessage.java b/common/src/main/java/dev/architectury/networking/simple/BaseC2SMessage.java new file mode 100644 index 00000000..0676a816 --- /dev/null +++ b/common/src/main/java/dev/architectury/networking/simple/BaseC2SMessage.java @@ -0,0 +1,41 @@ +/* + * 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.simple; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; + +/** + * The base class for client -> server messages managed by a {@link SimpleNetworkManager}. + */ +public abstract class BaseC2SMessage extends Message { + /** + * Sends this message to the server. + */ + @Environment(EnvType.CLIENT) + public final void sendToServer() { + if (Minecraft.getInstance().getConnection() != null) { + Minecraft.getInstance().getConnection().send(toPacket()); + } else { + throw new IllegalStateException("Unable to send packet to the server while not in game!"); + } + } +} \ No newline at end of file diff --git a/common/src/main/java/dev/architectury/networking/simple/BaseS2CMessage.java b/common/src/main/java/dev/architectury/networking/simple/BaseS2CMessage.java new file mode 100644 index 00000000..b566cfb5 --- /dev/null +++ b/common/src/main/java/dev/architectury/networking/simple/BaseS2CMessage.java @@ -0,0 +1,90 @@ +/* + * 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.simple; + +import net.minecraft.network.protocol.Packet; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.chunk.LevelChunk; + +/** + * The base class for server -> client messages managed by a {@link SimpleNetworkManager}. + */ +public abstract class BaseS2CMessage extends Message { + private void sendTo(ServerPlayer player, Packet packet) { + if (player == null) { + throw new NullPointerException("Unable to send packet '" + getType().getId() + "' to a 'null' player!"); + } + + player.connection.send(packet); + } + + /** + * Sends this message to a player. + * + * @param player the player + */ + public final void sendTo(ServerPlayer player) { + sendTo(player, toPacket()); + } + + /** + * Sends this message to multiple players. + * + * @param players the players + */ + public final void sendTo(Iterable players) { + Packet packet = toPacket(); + + for (ServerPlayer player : players) { + sendTo(player, packet); + } + } + + /** + * Sends this message to all players in the server. + * + * @param server the server + */ + public final void sendToAll(MinecraftServer server) { + sendTo(server.getPlayerList().getPlayers()); + } + + /** + * Sends this message to all players in a level. + * + * @param level the level + */ + public final void sendToLevel(ServerLevel level) { + sendTo(level.players()); + } + + /** + * Sends this message to all players listening to a chunk. + * + * @param chunk the listened chunk + */ + public final void sendToChunkListeners(LevelChunk chunk) { + Packet packet = toPacket(); + ((ServerChunkCache) chunk.getLevel().getChunkSource()).chunkMap.getPlayers(chunk.getPos(), false).forEach(e -> sendTo(e, packet)); + } +} \ No newline at end of file diff --git a/common/src/main/java/dev/architectury/networking/simple/Message.java b/common/src/main/java/dev/architectury/networking/simple/Message.java new file mode 100644 index 00000000..c63e5a50 --- /dev/null +++ b/common/src/main/java/dev/architectury/networking/simple/Message.java @@ -0,0 +1,70 @@ +/* + * 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.simple; + +import dev.architectury.networking.NetworkManager; +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; + +/** + * The base class for messages managed by a {@link SimpleNetworkManager}. + * + * @see BaseC2SMessage + * @see BaseS2CMessage + */ +public abstract class Message { + Message() { + } + + /** + * Returns the {@link MessageType} of this message + * + * @return the {@link MessageType} of this message + * @see SimpleNetworkManager#registerC2S(String, MessageDecoder) + * @see SimpleNetworkManager#registerS2C(String, MessageDecoder) + */ + public abstract MessageType getType(); + + /** + * Writes this message to a byte buffer. + * + * @param buf the byte buffer + */ + public abstract void write(FriendlyByteBuf buf); + + /** + * Handles this message when it is received. + * + * @param context the packet context for handling this message + */ + public abstract void handle(NetworkManager.PacketContext context); + + /** + * Converts this message into a corresponding vanilla {@link Packet}. + * + * @return the converted {@link Packet} + */ + public final Packet toPacket() { + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + write(buf); + return NetworkManager.toPacket(getType().getSide(), getType().getId(), buf); + } +} \ No newline at end of file diff --git a/common/src/main/java/dev/architectury/networking/simple/MessageDecoder.java b/common/src/main/java/dev/architectury/networking/simple/MessageDecoder.java new file mode 100644 index 00000000..c7e42ac9 --- /dev/null +++ b/common/src/main/java/dev/architectury/networking/simple/MessageDecoder.java @@ -0,0 +1,55 @@ +/* + * 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.simple; + +import dev.architectury.networking.NetworkManager; +import net.minecraft.network.FriendlyByteBuf; + +/** + * Decodes a {@link Message} from a {@link FriendlyByteBuf}. + * + * @param the message type handled by this decoder + * @author LatvianModder + */ +@FunctionalInterface +public interface MessageDecoder { + /** + * Decodes a {@code T} message from a byte buffer. + * + * @param buf the byte buffer + * @return the decoded instance + */ + T decode(FriendlyByteBuf buf); + + /** + * Creates a network receiver from this decoder. + * + *

The returned receiver will first {@linkplain #decode(FriendlyByteBuf) decode a message} + * and then call {@link Message#handle(NetworkManager.PacketContext)} on the decoded message. + * + * @return the created receiver + */ + default NetworkManager.NetworkReceiver createReceiver() { + return (buf, context) -> { + Message packet = decode(buf); + context.queue(() -> packet.handle(context)); + }; + } +} \ No newline at end of file diff --git a/common/src/main/java/dev/architectury/networking/simple/MessageType.java b/common/src/main/java/dev/architectury/networking/simple/MessageType.java new file mode 100644 index 00000000..287713db --- /dev/null +++ b/common/src/main/java/dev/architectury/networking/simple/MessageType.java @@ -0,0 +1,89 @@ +/* + * 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.simple; + +import dev.architectury.networking.NetworkManager; +import net.minecraft.resources.ResourceLocation; + +import java.util.Objects; + +/** + * A unique type for a message in a {@link SimpleNetworkManager}. + */ +public final class MessageType { + private final SimpleNetworkManager manager; + private final ResourceLocation id; + private final NetworkManager.Side side; + + MessageType(SimpleNetworkManager manager, ResourceLocation id, NetworkManager.Side side) { + this.manager = manager; + this.id = id; + this.side = side; + } + + /** + * Returns the network manager that manages this message type + * + * @return the network manager that manages this message type + */ + public SimpleNetworkManager getManager() { + return manager; + } + + /** + * Returns the ID of this message type + * + * @return the ID of this message type + */ + public ResourceLocation getId() { + return id; + } + + /** + * Returns the network side of this message type + * + * @return the network side of this message type + */ + public NetworkManager.Side getSide() { + return side; + } + + @Override + public String toString() { + return id.toString() + ":" + side.name().toLowerCase(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass()) { + return false; + } + + MessageType messageType = (MessageType) o; + return id.equals(messageType.id) && side == messageType.side; + } + + @Override + public int hashCode() { + return Objects.hash(id, side); + } +} \ No newline at end of file diff --git a/common/src/main/java/dev/architectury/networking/simple/SimpleNetworkManager.java b/common/src/main/java/dev/architectury/networking/simple/SimpleNetworkManager.java new file mode 100644 index 00000000..0955dde7 --- /dev/null +++ b/common/src/main/java/dev/architectury/networking/simple/SimpleNetworkManager.java @@ -0,0 +1,82 @@ +/* + * 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.simple; + +import dev.architectury.networking.NetworkManager; +import dev.architectury.platform.Platform; +import dev.architectury.utils.Env; +import net.minecraft.resources.ResourceLocation; + +/** + * A simple wrapper for {@link NetworkManager} to make it easier to register messages and send them to clients/servers. + */ +public class SimpleNetworkManager { + /** + * Creates a new {@code SimpleNetworkManager}. + * + * @param namespace a unique namespace for the messages ({@link #namespace}) + * @return the created network manager + */ + public static SimpleNetworkManager create(String namespace) { + return new SimpleNetworkManager(namespace); + } + + /** + * The unique namespace for the messages managed by this manager. + * This will typically be a mod ID. + */ + public final String namespace; + + private SimpleNetworkManager(String namespace) { + this.namespace = namespace; + } + + /** + * Registers a server -> client message. + * + * @param id a unique ID for the message, must be a valid value for {@link ResourceLocation#getPath} + * @param decoder the message decoder for the message + * @return a {@link MessageType} describing the registered message + */ + public MessageType registerS2C(String id, MessageDecoder decoder) { + MessageType messageType = new MessageType(this, new ResourceLocation(namespace, id), NetworkManager.s2c()); + + if (Platform.getEnvironment() == Env.CLIENT) { + NetworkManager.NetworkReceiver receiver = decoder.createReceiver(); + NetworkManager.registerReceiver(NetworkManager.s2c(), messageType.getId(), receiver); + } + + return messageType; + } + + /** + * Registers a client -> server message. + * + * @param id a unique ID for the message, must be a valid value for {@link ResourceLocation#getPath} + * @param decoder the message decoder for the message + * @return a {@link MessageType} describing the registered message + */ + public MessageType registerC2S(String id, MessageDecoder decoder) { + MessageType messageType = new MessageType(this, new ResourceLocation(namespace, id), NetworkManager.c2s()); + NetworkManager.NetworkReceiver receiver = decoder.createReceiver(); + NetworkManager.registerReceiver(NetworkManager.c2s(), messageType.getId(), receiver); + return messageType; + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 7c3b9bb6..b43627a8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ cf_type=release archives_base_name=architectury archives_base_name_snapshot=architectury-snapshot -base_version=2.1 +base_version=2.2 maven_group=dev.architectury fabric_loader_version=0.11.6 diff --git a/testmod-common/src/main/java/dev/architectury/test/TestMod.java b/testmod-common/src/main/java/dev/architectury/test/TestMod.java index 0f94d0c1..817ba63f 100644 --- a/testmod-common/src/main/java/dev/architectury/test/TestMod.java +++ b/testmod-common/src/main/java/dev/architectury/test/TestMod.java @@ -27,6 +27,7 @@ import dev.architectury.test.debug.client.ClientOverlayMessageSink; import dev.architectury.test.entity.TestEntity; import dev.architectury.test.events.DebugEvents; import dev.architectury.test.gamerule.TestGameRules; +import dev.architectury.test.networking.TestModNet; import dev.architectury.test.particle.TestParticles; import dev.architectury.test.registry.TestRegistries; import dev.architectury.test.registry.client.TestKeybinds; @@ -50,6 +51,7 @@ public class TestMod { TestTags.initialize(); TestTrades.init(); TestParticles.initialize(); + TestModNet.initialize(); if (Platform.getEnvironment() == Env.CLIENT) { initializeClient(); } diff --git a/testmod-common/src/main/java/dev/architectury/test/networking/ButtonClickedMessage.java b/testmod-common/src/main/java/dev/architectury/test/networking/ButtonClickedMessage.java new file mode 100644 index 00000000..ad1fb596 --- /dev/null +++ b/testmod-common/src/main/java/dev/architectury/test/networking/ButtonClickedMessage.java @@ -0,0 +1,57 @@ +/* + * 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.test.networking; + +import dev.architectury.networking.NetworkManager; +import dev.architectury.networking.simple.BaseC2SMessage; +import dev.architectury.networking.simple.MessageType; +import net.minecraft.Util; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.TextComponent; + +public class ButtonClickedMessage extends BaseC2SMessage { + private final int buttonId; + + /** + * To send this message from client to server, call new ButtonClickedMessage(id).sendToServer() + */ + public ButtonClickedMessage(int id) { + buttonId = id; + } + + public ButtonClickedMessage(FriendlyByteBuf buf) { + buttonId = buf.readVarInt(); + } + + @Override + public MessageType getType() { + return TestModNet.BUTTON_CLICKED; + } + + @Override + public void write(FriendlyByteBuf buf) { + buf.writeVarInt(buttonId); + } + + @Override + public void handle(NetworkManager.PacketContext context) { + context.getPlayer().sendMessage(new TextComponent("You clicked button #" + buttonId), Util.NIL_UUID); + } +} \ No newline at end of file diff --git a/testmod-common/src/main/java/dev/architectury/test/networking/SyncDataMessage.java b/testmod-common/src/main/java/dev/architectury/test/networking/SyncDataMessage.java new file mode 100644 index 00000000..942b4b47 --- /dev/null +++ b/testmod-common/src/main/java/dev/architectury/test/networking/SyncDataMessage.java @@ -0,0 +1,60 @@ +/* + * 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.test.networking; + +import dev.architectury.networking.NetworkManager; +import dev.architectury.networking.simple.BaseS2CMessage; +import dev.architectury.networking.simple.MessageType; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.TextComponent; + +public class SyncDataMessage extends BaseS2CMessage { + private final CompoundTag serverData; + + /** + * To send this message, call new SyncDataMessage(tag).sendToPlayer(player) / sendToAll(server) / etc. + * + * @see BaseS2CMessage + */ + public SyncDataMessage(CompoundTag tag) { + serverData = tag; + } + + public SyncDataMessage(FriendlyByteBuf buf) { + serverData = buf.readAnySizeNbt(); + } + + @Override + public MessageType getType() { + return TestModNet.SYNC_DATA; + } + + @Override + public void write(FriendlyByteBuf buf) { + buf.writeNbt(serverData); + } + + @Override + public void handle(NetworkManager.PacketContext context) { + context.getPlayer().sendMessage(new TextComponent("Received data from server: " + serverData), Util.NIL_UUID); + } +} \ No newline at end of file diff --git a/testmod-common/src/main/java/dev/architectury/test/networking/TestModNet.java b/testmod-common/src/main/java/dev/architectury/test/networking/TestModNet.java new file mode 100644 index 00000000..e9ee209f --- /dev/null +++ b/testmod-common/src/main/java/dev/architectury/test/networking/TestModNet.java @@ -0,0 +1,37 @@ +/* + * 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.test.networking; + +import dev.architectury.networking.simple.MessageType; +import dev.architectury.networking.simple.SimpleNetworkManager; +import dev.architectury.test.TestMod; + +public interface TestModNet { + SimpleNetworkManager NET = SimpleNetworkManager.create(TestMod.MOD_ID); + + // An example Client to Server message + MessageType BUTTON_CLICKED = NET.registerC2S("button_clicked", ButtonClickedMessage::new); + + // An example Server to Client message + MessageType SYNC_DATA = NET.registerS2C("sync_data", SyncDataMessage::new); + + static void initialize() { + } +} \ No newline at end of file