Browse Source

Added SimpleNetworkManager (#120)

* Added SimpleNetworkManager

* Implemented requested changes for SimpleNetworkManager

* Add javadoc to networking.simple

* Finish up PacketID docs

* Fix javadoc building

* Added license headers

* Renamed BasePacket to Message and PacketID to MessageType

* PacketDecoder -> MessageDecoder

Signed-off-by: shedaniel <daniel@shedaniel.me>

* Rename constructor parameters

Signed-off-by: shedaniel <daniel@shedaniel.me>

* Final touches

Signed-off-by: shedaniel <daniel@shedaniel.me>

* Apply suggestions from code review

Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com>

* Fixes to the javadocs

Signed-off-by: shedaniel <daniel@shedaniel.me>

* Bump to 1.20

Signed-off-by: shedaniel <daniel@shedaniel.me>

Co-authored-by: Juuz <6596629+Juuxel@users.noreply.github.com>
Co-authored-by: shedaniel <daniel@shedaniel.me>
Kristiāns Micītis 4 years ago
parent
commit
289dd026f2

+ 6 - 0
build.gradle

@@ -52,6 +52,12 @@ allprojects {
         }
     }
 
+    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")
 

+ 41 - 0
common/src/main/java/me/shedaniel/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 me.shedaniel.architectury.networking.simple;
+
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.Minecraft;
+
+/**
+ * The base class for client -&gt; 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!");
+        }
+    }
+}

+ 90 - 0
common/src/main/java/me/shedaniel/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 me.shedaniel.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 -&gt; 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<ServerPlayer> 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));
+    }
+}

+ 70 - 0
common/src/main/java/me/shedaniel/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 me.shedaniel.architectury.networking.simple;
+
+import io.netty.buffer.Unpooled;
+import me.shedaniel.architectury.networking.NetworkManager;
+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);
+    }
+}

+ 55 - 0
common/src/main/java/me/shedaniel/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 me.shedaniel.architectury.networking.simple;
+
+import me.shedaniel.architectury.networking.NetworkManager;
+import net.minecraft.network.FriendlyByteBuf;
+
+/**
+ * Decodes a {@link Message} from a {@link FriendlyByteBuf}.
+ *
+ * @param <T> the message type handled by this decoder
+ * @author LatvianModder
+ */
+@FunctionalInterface
+public interface MessageDecoder<T extends Message> {
+    /**
+     * 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.
+     *
+     * <p>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));
+        };
+    }
+}

+ 89 - 0
common/src/main/java/me/shedaniel/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 me.shedaniel.architectury.networking.simple;
+
+import me.shedaniel.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);
+    }
+}

+ 82 - 0
common/src/main/java/me/shedaniel/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 me.shedaniel.architectury.networking.simple;
+
+import me.shedaniel.architectury.networking.NetworkManager;
+import me.shedaniel.architectury.platform.Platform;
+import me.shedaniel.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 -&gt; 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<BaseS2CMessage> 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 -&gt; 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<BaseC2SMessage> 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;
+    }
+}

+ 1 - 1
gradle.properties

@@ -6,7 +6,7 @@ supported_version=1.16.4/5
 
 archives_base_name=architectury
 archives_base_name_snapshot=architectury-snapshot
-base_version=1.19
+base_version=1.20
 maven_group=me.shedaniel
 
 fabric_loader_version=0.11.1

+ 2 - 0
testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java

@@ -27,6 +27,7 @@ import me.shedaniel.architectury.test.debug.client.ClientOverlayMessageSink;
 import me.shedaniel.architectury.test.entity.TestEntity;
 import me.shedaniel.architectury.test.events.DebugEvents;
 import me.shedaniel.architectury.test.gamerule.TestGameRules;
+import me.shedaniel.architectury.test.networking.TestModNet;
 import me.shedaniel.architectury.test.particle.TestParticles;
 import me.shedaniel.architectury.test.registry.TestRegistries;
 import me.shedaniel.architectury.test.registry.client.TestKeybinds;
@@ -47,6 +48,7 @@ public class TestMod {
         TestTags.initialize();
         TestTrades.init();
         TestParticles.initialize();
+        TestModNet.initialize();
         if (Platform.getEnvironment() == Env.CLIENT) {
             TestKeybinds.initialize();
             EntityRenderers.register(TestEntity.TYPE, MinecartRenderer<TestEntity>::new);

+ 57 - 0
testmod-common/src/main/java/me/shedaniel/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 me.shedaniel.architectury.test.networking;
+
+import me.shedaniel.architectury.networking.NetworkManager;
+import me.shedaniel.architectury.networking.simple.BaseC2SMessage;
+import me.shedaniel.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);
+    }
+}

+ 60 - 0
testmod-common/src/main/java/me/shedaniel/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 me.shedaniel.architectury.test.networking;
+
+import me.shedaniel.architectury.networking.NetworkManager;
+import me.shedaniel.architectury.networking.simple.BaseS2CMessage;
+import me.shedaniel.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);
+    }
+}

+ 37 - 0
testmod-common/src/main/java/me/shedaniel/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 me.shedaniel.architectury.test.networking;
+
+import me.shedaniel.architectury.networking.simple.MessageType;
+import me.shedaniel.architectury.networking.simple.SimpleNetworkManager;
+import me.shedaniel.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() {
+    }
+}